🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。 ``` function Box () {} Box.prototype.name = 'Lee' Box.prototype.age = 100 Box.prototype.run = function () { return this.name + this.age } ``` 比较一下原型内的方法地址是否一致: ``` var box1 = new Box() var box2 = new Box() box1.run === box2.run // true box1.name === box2.name // true box1.name = 'Jack' box2.name // 'Jack' ``` 为了更进一步了解构造函数的声明方式和原型模式的声明方式,我们通过图示来了解一下: ![构造函数方式](images/screenshot_1517980984371.png) ![原型模式方式](images/screenshot_1517981104466.png) *** 构造函数上有一个prototype属性,这个属性是一个对象 构造函数产生的实例上有一个__proto__属性,这个属性指向prototype属性 *** 在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。通过这两个属性,就可以访问到原型里的属性和方法了。 PS:IE浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别。虽然可以输出,但无法获取内部信息。 ## isPrototypeOf 判断一个对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。 Box.prototype.isPrototypeOf(box) 原型模式的执行流程: 1. 先查找构造函数实例的属性或方法,如果有,立刻返回; 2. 如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回 ## hasOwnProperty 判断一个属性是在构造函数的实例里,还是在原型里?可以使用hasOwnProperty()函数来验证: box.hasOwnProperty('name') // 实例里面有返回true,否则返回false ## in in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中 'name' in box // true,存在实例中或原型中 ## 判断属性是否只存在于原型中 ``` function isProperty (object, property) { return !object.hasOwnProperty(property) && (property in object) } ``` ## 字面量方式 ``` function Box () {} Box.prototype = { name: 'Lee', age: 100, run: function () { return this.name + this.age } } ``` 使用构造函数创建原型对象和使用字面量创建对象在使用上基本形同,但还是有一些区别,字面量创建的方式使用constructor属性不会指向实例,而是指向了Object,构造函数创建的方式责罚相反 ``` var box = new Box() box instanceof Box // true box instanceof Object // box.constructor === Box box.constructor === Object ``` 强制指向Box ``` Box.prototype = { constructor: Box } ``` ## 构造函数和原型搭配 原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一样的,而原型最大的缺点就是它最大的优点,那就是共享。 原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以,但如果属性包含引用类型,就存在一定的问题:某个实例修改了引用属性,则其他实例的引用属性也会被修改。 ``` function Box (name ,age) { this.name = name this.age = age this.family = ['父亲', '母亲', '妹妹'] } Box.prototype = { constructor: Box, run: function () { return this.name + this.age + this.family } } ``` 这种混合模式很好的解决了传参和引用共享的两大难题,是创建对象比较好的方法。 ## 动态原型模式 原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型模式让人感觉很怪异,最好的就是把构造函数和原型封装到一起,动态原型模式可以解决这个问题 ``` function Box (name ,age) { this.name = name this.age = age // 原型只需要初始化一次 if (typeof this.run === 'undefined') { Box.prototype.run = function () { return this.name + this.age } } } ```