假设我们在编写一个飞机大战的网页游戏。某种飞机拥有分身技能,当它使用分身技能的时 候,要在页面中创建一些跟它一模一样的飞机。如果不使用原型模式,那么在创建分身之前,无 疑必须先保存该飞机的当前血量、炮弹等级、防御等级等信息,随后将这些信息设置到新创建的 飞机上面,这样才能得到一架一模一样的新飞机。如果使用原型模式,我们只需要调用负责克隆的方法,便能完成同样的功能。
js中调用 Object.create()即可克隆对象。
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name){ this.name=name } Person.prototype.say = function(){ console.log(this.name); } var me = new Person('ltinyho') var myClone = Object.create(me) myClone.say(); var myClone2 = {} myClone2.__proto__ = me; myClone2.say();
|
原型模式一定有一个根对象,其他的对象一定是从某个已经存在的对象生成的,然后在添加它自己的属性和方法。在这些对象上就会形成一条原型链,当调用某个对象的方法时,如果在它本身没有这个方法,那么就会从原型链一直向上找,直到根对象为止。
js中的根对象为Object.prototype
,它是这个空对象。
1 2
| var a = {} Object.getPrototypeOf(a)===Object.prototype
|
构造函数
- 创建一个对象
- 将构造函数的作用域赋给新对象。(因此this指向了新对象)
- 执行构造函数中的代码。(为这个新对象添加属性)
- 如果不调用return ,默认返回这个对象,显式的return一个对象会影响结果,返回原始值不会。
对象的原型链是如何查找的呢?
要完成查找,每个对象首先至少得记住自己的原型,知道自己从哪里来的。js
给对象提供了一个名为__proto__
的隐藏属性,某个对象的 __proto__
属性默认指向它的构造器原型对象,即 {Constructor}.prototype
,当在自己找不到属性时,就会到这个对象上查找。但是一个对象的 Constructor
可能会被修改。原因是 js
中每个对象都是从 Object.prototype
对象克隆而来的,如果这样的话,只能得到单一的继承关系,即每个对象都是继承于 Object.protoype
。但是对象构造器的原型并不仅限于 Object.prototype
上,而是可以动态的指向其他对象。看一下以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function A(){ } A.prototype.name= 'A' A.prototype.say = function(){ console.log(this.name); } function B(){ } var a = new A() B.prototype = a; var b= new B() console.log(b.name); b.__proto__===B.prototype
|
- 首先遍历 b 上的所有属性,没有找到 name 。
- 查找 name 属性的请求被委托给 b 的
b.__proto__
, b.__proto__
记录了构造器原型 B.prototype
,而B.prototype
指向一个通过 new A()
创建的对象。
- 然后就到 a 的 属性上遍历,还是没有。然后到
a.__proto__
记录的 A.prototype
上找的构造器原型上找,找到了。
注意:当在本身查找不到时,是先到 __proto__
上去找, 如果一个对象已经创建,__proto__
确定了,这时改变 这个对象的 constructor
并不会改变原型链。
this, call, apply, bind
JavaScript 的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this的指向大致分为四种
- 对象的方法调用
- 普通函数调用
- 构造函数调用
- Function.prototype.call和Function.prototype.apply调用
call和apply
call和apply可以修改函数内部的this指向,它们的区别只在于参数的不同。第一个参数都为this指向的对象,第二个参数call接收一个参数,apply则以数组的形式接收。
call和apply的用途
- 改变this的指向
- 实现bind方法
- 借用其他对象的方法
bind的实现
1 2 3 4 5 6
| Function.prototype.bind = function(){ var self = this, context = [].shift.call( arguments ),args = [].slice.call( arguments ); return function(){ return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) ); } };
|
从上面可以看出,bind 会返回一个新的函数,并且可以传入参数,作为新生成函数的预置的参数。