以前在网上搜了一些关于js原型的资料,看了过后,是有一部分理解,但是还不够全面,在这我就写写下关于js的原型。
看了下js高级程序设计,我对js原型的有了一定的理解。里面谈到我们创建对象时,如果一直手写一些相似的对象或者说里面有重复的属性时,会非常麻烦。列如:
let a = { name: ‘a‘, idnex: 1 } let b = { name: ‘b‘, idnex: 2 }
为了解决这种问题,就出来了一些能创建对象的模式。
//工厂模式 function createObj (name, index) { let o = {} o.name = name o.index = index o.sayName = function () { alert(this.name) } return o } let a = createObj(‘a‘, 1) let b = createObj(‘b‘, 2)
这种模式,虽然解决创建多个相似对象的问题, 但却没有解决对象识别的问题(即怎样知道一个对象的类型)。对于这句话,我的理解是,这样创建的对象,在类型上区别不出来吧,这种模式产生的对象,感觉都是同一层面上的,都是通过 new Object 出来的(而原型是可以通过new CreateObj 等构造名创建)
随着js发展,构造函数模式就出来了
//构造函数模式 function CreateObj (name, index) { this.name = name this.index = index this.sayName = function () { alert(this.name) } } let a = new CreateObj(‘a‘, 1) let a = new CreateObj(‘b‘, 2)
这种模式的问题就是每个方法都在实例上创建一遍,如上面的sayName.
我们可以把方法提取出来:
function CreateObj (name, index) { this.name = name this.index = index this.sayName = sayName } function sayName () { alert(this.name) } let a = new CreateObj(‘a‘, 1) let a = new CreateObj(‘b‘, 2)
这样的话,所有的实例都会共有这个sayName方法。但是也有新的问题,这样定义的方法是全局的,如果定义多个方法,命名都会要让人崩溃的,也会造成覆盖的风险。
对以上的这些问题,就可以用原型来解决。
//原型模式 function A () { } A.prototype.name = ‘a‘ A.prototype.index = 1 A.prototype.syaName = function () { console.log(this.name) } //原型对象:{name: a, index: 1, syaName: function () {console.log(this.name) } } let a1 = new A() let a2 = new A()
这里用了prototyope(原型,js仅仅会在函数给它这个属性),这里 A.prototype是指向一个对象的,你就当它就是一个原型对象。上面创建了一个原型对象且包含属性name、index、sayName,然后通过它new出来的实例,本身会有一个[[prototype]]属性,在js中这是个内部属性,一般不能直接访问,但是在Firefox、Safari、Chrome在每个对象上都支持一个属性_proto_,这个可以理解成这个内部属性。 [[prototype]]这个属性指向的就是它的原型对象,但是这里要注意的是,在原型机制中,只会访问实例本身没有的属性,才会再去找原型对象中是否有这个属性,如:
function A () { } A.prototype.name = ‘a‘ A.prototype.index = 1 A.prototype.syaName = function () { console.log(this.name) }
A.prototype.c = [1, 34]
let a1 = new A()
let a2 = new A()
console.log(a1.a) // 1
console.log(a2.a) // 1
console.log(a2.c) // [1, 34]
console.log(a2.c) // [1, 34]
a2.a = 2
a2.c.push(‘3‘)
console.log(a1.a) // 1
console.log(a2.a) // 2
console.log(a2.c) // [1, 34, 3]
console.log(a2.c) // [1, 34, 3]
这里可能有人会疑惑,为什么a1中的a不会改变,c改变了。先说下整个过程:
a1、a2都没有属性a,c ,所以它就去访问它的原型对象,如果都是只是访问的话,因为访问的是同一个对象,所以就得到同样的值。
然后, a2.a = 2 这个赋值操作,是会优先对a2这个对象进行赋a这个属性的(那么a2就会有自己的属性a并且值为2),而不是去对原型对象的属性a重新赋值。而a2.c不是赋值操作,只是对元数据进行修改,因为a2里面没有c属性,也修改不了自己的属性c,那么就会对原型对象里面的数据进行修改了。我们可以验证一下:
a2.a = 2 a2.c = [1, 2] console.log(a1.a) // 1 console.log(a2.a) // 2 console.log(a2.c) // [1, 34] console.log(a2.c) // [1, 2]
这里都是给自己的实例对象添加了各自的属性,而原型对象并没有改变,所以他们自己有的属性就访问自己的,没有的就访问没改变的原型对象。
简而言之,js中每个对象会有一个内部属性[[prototype]],该属性指向它的原型对象。
而原型对象,可以通过函数的prototype来创建以及给它赋属性。
每个通过原型构造函数产生的实例对象,是共享同一个原型对象的,该对象的属性值是不能通过实例赋值修改的。