前端开发工具
1.1、 WebStorm介绍和下载
l 介绍
WebStorm是JetBrains 推出的一款强大的HTML5编辑工具,拥有丰富的代码快速编辑,可以智能的补全代码、代码格式化、html提示以及代码检查和快速修复等,支持不同浏览器的提示,同时也是一款JavaScript 开发工具,拥有即时编辑(chrome)、自动完成、debugger、Zen Coding、HTML5 支持、JSLint、Less支持、CoffeeScript支持、Node.JS、单元测试、集成git和svn版本控制等特性,推荐前端工程师使用。
WebStorm被广大中国JS开发者誉为“Web前端开发神器”
l 官网:http://www.jetbrains.com/webstorm/
js的基础回顾
// 1、JS中数据类型有哪些?
// JS中有6种数据类型,其中五种基本数据类型(Number,Boolean,String,Undefined,Null)和一种复合数据类型(Object)
console.log('--------------------JS中数据类型有哪些END------------------------');
// 2、如何判断变量数据的类型?
// 使用typeof来判断变量的数据类型
var i = 12; // 'number'
var s = 'abc'; // 'string'
var b = true; // 'boolean'
var u = undefined; // undefined
var oDate = new Date(); // 'object'
var arr = [12,5]; // 'object'
var o = {}; // 'object'
var fn = function(){alert('xxx')}; // 'function'
var n = null; // 'object'
console.log(typeof i);
console.log(typeof s);
console.log(typeof b);
console.log(typeof u);
console.log(typeof oDate);
console.log(typeof arr);
console.log(typeof o);
console.log(typeof fn);
console.log(typeof n);
// 注意点:
// 1. JS解释器认为null是object数据类型的一种特殊形式,而null在JS里面特指空对象
// 2. function(){}的数据类型是function, 也就是说function也是一种基本数据类型,而不是对象的一种特殊形式。实际上在JS中,函数是一个极为容易引起误解或者引发歧义的数据类型,它可以是独立的数据类型,也可以被称为类或者构造函数,还可以作为函数对象等。
console.log('--------------------如何判断变量数据的类型END------------------------');
// 3、JS中的空类型有哪些?
// 1).null 是一个原始的数据类型,只有一个值null, 特指空对象
// 2).undefined是一个原始的数据类型,只有一个值undefined
// 1、定义变量没有赋值,此时变量的值是undefined
var a;
console.log(a);// undefined
///2、函数没有返回值或者没有返回具体的值都是默认返回undefined
function show() {
console.log('我没有返回值,默认返回undefined');
}
console.log(show()); // undefined
///3、访问对象中一个不存在的属性
console.log(window.abc); // undefined
console.log('--------------------JS中的空类型有哪些END------------------------');
// 4、JS中代表哪些数据代表false?
// 数字0、空字符串''、false、空对象null、NaN、undefined
// 5、JS中 === 和 == 有什么区别?
// == 用来比较两个变量的值是否相等,先做数据类型的转换,在做比较
// === 全等,严格的比较,先比较数据类型,数据类型不相等,就立刻返回false
console.log('1' == 1); // true
console.log('1' === 1); // false
console.log('--------------------JS中 === 和 == 有什么区别END------------------------');
// 6、JS中的in运算符有什么用?
// 1). 用来循环数组,性能低,建议使用普通的for
var arr = [12,5,8];
for (var i in arr) {
console.log(i + '---' + arr[i]); // 0-12 1-5 2-8
}
// 2). 用来循环对象
var p = {
name: 'Jack',
age: 20,
gender: 'male'
};
for (var name in p) {
console.log(name + '---' + p[name]); // 'name---Jack' age---20 'gender---male'
}
// 属性操作:
// i). 点 . 比较方面,不能接收变量
// ii). 中括号 [] 可以接收变量,可以替代所有点出现的位置
// 3). 用来判断某个属性是否属于某一个对象
console.log('name' in p); // true
console.log('abc' in p); // false
console.log('--------------------JS中的in运算符有什么用END------------------------');
// 7、JS中的逻辑中断?
// 且&& 或 ||
// && 当左侧为false,直接返回左侧的结果;当左侧结果为true,返回右侧结果
// || 当左侧为true,直接返回左侧结果; 当左侧结果为false,返回右侧结果
console.log(1 || 2); // 1
console.log(0 || 2); // 2
console.log(null || 0); // 0
面向对象的介绍:那么什么是对象呢?这里只需要举出相关例子就是可以不需要准确说出概念
面向过程:完成一件事情的所有步骤都亲力亲为,一步一步来完成。例如要吃饺子,就必须自己剁馅儿、和面、擀饺子皮儿、包饺子
面向对象:完成一件事情,直接去找能做这件事的人。例如吃饺子,直接找老婆(如果没有就去饺子馆吧)
面向过程:是事件的执行者
面向对象:是指挥者
一言以蔽之:面向对象就是对面向过程的封装!
那么为什么要面向对象呢?
1) 提高代码的复用性,简化开发
2) 使代码结构清晰
3) 在JS中有一个特殊的作用:防止代码污染
那么什么是代码污染呢?代码污染其实就是代码中含有重复的变量并且其中后面的代码吧前面的代码给覆盖了,就是再调用的时候不知道找不到自己真实的需要调用的那个对象或者是方法
如何解决代码污染?
1、我们可以把定义的方法放在对象中里面,然后使用对象调用自己的方法基本上可以防止代码污染如下:
创建对象的两种方式:
第一种就是使用对象的直接量的方式:
在定义对象的时候,直接使用JSON格式来定义一个JS对象,并且为这个对象赋值属性和方法,称为对象的直接量(字面量)
这种方式创建的对象,默认是Object类型的对象(这种方式创建object对象)
l 案例代码:(也就是使用json格式的数据来定义对象称作为对象的直接量)(也就是使用json格式的数据来定义对象实现这个过程)
效果如下:
l 缺陷:
u 只能使用1次,无法重复利用,每次都要编写相同代码创建新对象
l 使用场景:
u 对象只需要一次使用的时候。比如作为传递参数;
u 自己封装JS框架(函数)的时候。
第二种方式就是使用构造函数创建对象
l 构造函数创建对象的一般步骤:
1) 通过function指定一个构造函数,与普通函数没差别,只是约定首字母大写以区分
2) 然后在构造函数中通过this.属性名=属性值来完成对象属性初始化。
3) 最后通过new关键字创建对象(实例)
其中的代码案例:
效果如下:
那么对象中的函数相同吗?
测试:
结果显示:
实际上查看内存中对象的属性发现p1和p2中各有一份 sayHello 方法:
结论:
通过上面的实验我们发现,对象中的所有属性,包括定义的方法,都是每个对象独有的,不会共享。
属性不共享可以理解,但是方法都是一样的,如果也不共享,那么就是对内存的极大浪费。
l 优势:
u 对象可以重复创建
l 缺点:
u 对象中相同的方法,无法实现共享,内存占用高
由上面可以引出来对象原型的概念
对象原型(prototype)
在JS中,每一个构造函数都会有一个prototype(也是一个对象)属性,任何由同一个构造函数创建的对象,都会共享这个prototype对象,并且可以具备这个prototype的所有属性(成员)
原型的基本的使用方法
刚开始的时候没有设置对象原型的时候,定义一个空的构造函数,只给出name属性,当打印age属性的时候结果是undefined,因为没有给属性
结果:
打印控制台中是undefined的。
查看对象属性也是只有name属性,没有age:
当使用原型给构造函数添加新的属性的时候,也就是使用prototype给构造函数添加新的属性
此时控制台上已经显示了属性
这个时候再次查看对象的属性:
发现p1和p2对象中虽然依然没有age,但是构造函数Person的prototype中已经有age了,又因为对象会共享prototype的成员,所以每个对象都可以使用age属性了!
例外一种情况进行总结:
1.1.1.1、 给对象从原型中继承的属性赋值
尝试修改对象中,继承自prototype的属性值age:
结果:p1的age已经变为30, p2没变
那么问题来了:
问题,既然是共享的属性,那么为什么p1修改了,p2值没变?
查看内存中的状态:
我们发现:prototype中的age没有改变,而是在对象p1中新增了自己的age属性,并且赋值为30
结论:当原型和对象自身都有相同属性时,对象中的属性优先!
1.1.1.1、 总结
1) 当我们访问对象中的属性时,默认会先在当前对象中查找;如果在当前对象找不到,就会去对象的prototype中查找
a) 事实上,prototype也是对象,因此在prototype对象中找不到,会到prototype对象的prototype中找,逐级向上形成“原型链”,原型链的最顶级的是Object.prototype
b) 那么Object.prototype也是对象,再向上还有吗?没有了,再向上是null。Object.prototype就是原型链的根
2) 修改对象的属性,不会修改prototype中的属性,而是在对象中创建新的属性
3) 某些浏览器支持__proto__属性,与prototype是同一个东西,但是某些浏览器还不支持。所以一般不要用
再有一种情况就是通过原型来更改构造函数
为了减少对象内存占用,我们一般创建构造函数的时候,会把经常变化的属性在构造函数中声明,每个对象都有自己的属性。然后把公用的不变化的东西(通常是函数)通过原型来添加。
此时的验证结果显示:
每个对象都可以调用sayHello方法,证明共享了方法。 各个对象的sayHello方法判断是相等的。证明内存中只有一份。
总结构造函数创建对象的方法:
1、 构造函数里面写变化属性
2、 原型上去添加方法
通过原型扩展js的原生对象
事实上不仅仅我们自己定义的对象有原型,JS中原生的对象也有原型。我们可以通过原生对象的原型对原生的JS进行扩展!
原生的JS中,Array对象并没有sum方法,尝试调用报错:
我们使用原型进行扩展:
结果显示:
疑问?
扩展字符串的trim方法
终极总结:判断一个 对象的类型
那么如何判断一个对象的类型呢?
1)instance of
2)通过类的字节码Class对象
方式一:instanceof作用是用来判断一个对象是否属于一个类
案例代码:
结果显示:
由此得出的总结结论:instanceof 可以基本准确的判断对象的所属类型,但是当 instanceof后面跟的是类型是父类类型的时候,判断是不准确的。这一点与Java类似
如果要是更准确的来判断对象类型的话,是可以通过类的字节码对象,也就是和java中的反射技术是差不多的,但是js中是没有类的概念的,更不要说class对象,但是js中类似的概念是构造函数constructor
也就是所说的方式2 :作用constructor用来检测直接的构造者
通过constructor来准确判断对象类型
结果显示的也是返回的是布尔值
this关键字的总结介绍:
在Java中,this关键字表示的是当前对象。比如在方法中使用,就是调用方法的对象。
在JavaScript中,this也是类似的。只不过JS中方法无需定义在类中,所以很多人对this的含义就懵逼了。
代码案例的实现:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>this关键字的含义</title>
</head>
<body>
<script>
// 演示: this关键字的含义
// this指的是当前的方法属于谁,只看调用,谁调用就指向谁,所以我们只需要搞清楚调用方法的对象即可
// 1. 全局函数中的this
function show() {
console.log('全局函数中的this:' + this);
}
// 全局函数 默认是注册到window对象上中
// 而调用window中的任何成员,默认是可以省略window.的
// 这里的show()完整的写法其实是:window.show(); 因此肯定当前的方法属于window这个对象
// show(); // Object window
window.show();
// 2. 对象中的this
var p = {
name: 'Jack',
age: 20,
gender: 'male',
sayHello: function() {
console.log('Hello, I am' + this.name + ',' + this.age + 'years old');
}
};
// 对象中的this,指向的是调用该方法的对象,现在该方法属于p这个对象,因此this指向了p
p.sayHello(); // 'Hello, I amJack,20years old'
// 3. 事件函数中的this
// 当前的事件注册到了document对象身上
// 事件触发时,调用该事件处理函数的对象就是事件源对象,也就是document对象,此时事件函数里面的this指向了事件源对象
document.onclick = function() {
console.log('事件函数中的this:' + this); //object HTMLDocument
};
// 4. 定时器里面的this
// setTimeout方法本身就是window对象的一个方法,那么时间到了以后,
// 定时器里面的函数有谁来调用? window, 所以定时器里面的this就是window
setTimeout(function() {
console.log('定时器里面的this:' + this); // object Window
}, 1000);
</script>
</body>
</html>
关于this关键字进行总结:
1) 其实记住一点就够了:this指的是当前的方法属于谁,只看调用,谁调用就指向谁
2) 对象外的全局函数,this是window
3) 对象中的方法,被对象调用时,this是对象自己
4) 事件处理函数,this是事件绑定的对象
5) 定时任务函数,this是window
3.8 、对象的继承(组合式继承、构造函数继承、原型继承)
3.8.1 组合式继承:组合式继承就是把 一个对象的属性 全部加到另一个对象中。也被称为属性拷贝。
应用:组合式继承主要用在 对象的直接量 继承另一个 对象的直接量时。
案例:
结果显示:
使用的场景:比如我用对象封装了一个框架,现在要对该框架的方法进行扩展,新增更多方法。此时就可以把新增的方法定义到新的对象中,使用组合式继承来完成 方法的扩展。
//比如可以通过一个匿名对象,给o1可以扩展更多的方法
结果显示
3.8.2 构造函数继承
如果对象不是 直接量对象,而是通过构造函数new出来的,那么如何继承另一个构造函数中的属性呢?
结果显示:
我们发现直接在子类的构造函数中直接调用父类的构造函数不能实现属性的继承,因为直接调用父类的构造函数,此时父类的构造函数属于全局函数,所以里面的this指向了window对象,不是Worker的实例,这个时候我们要去改变父类构造函数里面this的指向,让父类构造函数里面的this指向Worker的实例
函数也是对象,任何函数都可以调用两个方法:call和apply ,来自于Function的原型
call和apply都会导致调用的函数被执行,但是可以在执行时指定this
名称 | 用法 | 参数说明 |
call | fn.call(fn函数里面this的值,参数1,参数2..); | 第一个参数是被调用函数里面this的值,真实的参数从第二个开始 |
apply | fn.apply(fn函数里面this的值, [参数1,参数2..]); | 同上,实际参数是数组结构 |
结果显示:
我们发现,worker具备了父类中的所有属性,但是,Person类在原型中扩展的方法 sayHello 并没有被继承。访问时报错
并且,在执行instanceof的时候,无法判断worker属于Person类的实例
特点:
1) 可以继承父类构造函数中定义的属性
2) 父类原型中定义的方法无法访问,扩展性差
3) instanceof 判断失效,因为子类原型与父类原型没有关联
3.8.3 使用的是原型继承;
为了解决上面的问题我们可以使用的就是原型继承的方式进行实现这个过程:
总结(非常重要可以查看)
JS的继承步骤:
1) 在你的构造函数中,调用父类的构造,但是要用call或apply
2) 让你的构造函数的prototype指向父类的对象
3) 因为原型被覆盖,因此原型中的constructor也被覆盖,需要修正constructor指向子类的构造函数
注意:原型继承,其实是让子类原型指向了父类对象。 这样父类对象就 覆盖了子类prototype中的所有属性。其中constructor属性也被覆盖成了父类的。因此,在使用原型继承时,必须手动把子类的原型中的constructor属性修改为子类的构造函数!