通过代码读懂apply,call,bind及JS原型链
原型链
使用new原型对象生成实例对象的缺点(原型链要解决的问题)
用原型对象生成实例对象,有一个缺点,那就是无法共享属性和方法。
例如,在Person对象的构造函数中,设置一个实例对象的共有属性species。1
2
3
4 function Person(name){
this.name = name;
this.species = '黄种人';
}
然后,生成两个实例对象:
1 | var personA = new Person('张三'); |
这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。
1 | personA.species = '黑人'; |
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
prototype属性(原型链)的引入
在了在来自同一原型对象的实例对象间共享数据(对象),于是在原型对象中引入了prototype属性。该属性包含一个对象(以下简称”prototype对象”),所有实例对象间需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
还是以Person为例,现在用prototype属性进行改写:1
2
3
4
5
6
7
8
9
10 function Person(name){
this.name = name;
}
Person.prototype = { species : '黄种人' };
var personA = new Person('张三');
var personB = new Person('Tom');
alert(personA.species); // 黄种人
alert(personB.species); // 黄种人
现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。1
2
3
4 Person.prototype.species = '黑人';
alert(personA.species); // 黑人
alert(personB.species); // 黑人
总结
由于源于同一原型对象的所有实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。
这就是Javascript原型链的由来,也是Javascript继承机制的核心思想。
apply,call及bind的使用
apply,call,bind的作用:改变某个函数运行时的上下文(context)环境,换句话说,就是为了改变函数体内部 this 指向。
调用格式:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
var newFunc = func.bind(this);
从上面可以看到:call和apply的作用相同,区别在于调用时传递的参数(apply第二个参数是数组格式)。bind的作用和call,apply相同,只是调用bind时不是立即执行func函数,而是返回新的函数对象,供后续代码调用。
看懂以下示例,几个方法的作用及区别就很明白了:
1 | var func = function(arg1, arg2) { |