在JS里面,原型链是一个非常重要的存在,它主要用于在继承过程中,子实例可以通过原型链访问到父类的属性。这一点跟JAVA中的extends相似。
说到原型链,有两个不得不说的属性:__proto__(两个下划线) 跟 prototype ,__proto__ 是所有对象都有的一个属性,而 prototype 是函数特有的一个对象,默认里面有个 constructor 属性,该属性是指向函数本身。
1 function fn() {} 2 fn.prototype.say = function() { 3 console.log(‘hello world‘) 4 } 5 var fn1 = new fn() 6 //在这个例子里面,fn.prototype 里面有个constructor 属性,该属性是指向自己的 7 //fn.prototype.constructor === fn 8 //而 fn1 是一个对象,没有 prototype 属性,但是有 __proto__ 属性,前面说过,子实例可以通过原型链访问到父对象的属性,而原型链就是通过 __proto__ 这个属性来维持的。 9 //而此时的原型链是这样的: 10 //fn1.__proto__ -> fn.prototype 11 //fn.prototype.__proto__ -> Object.prototype 12 //也就是说原型链是一级一级往上查找的,如果上一级不存在相应的属性,就会再往上一级查找,最终找到 Object 这个大boss,如果还是没有,则返回 undefined,但是如果fn1 本身就有该属性,就会拿自己的,不会再往上查找 13 //对于函数fn来说,它的共享属性都是定义在 prototype 对象里面的,也就是说只要是定义在这个对象里面的东西,它的实例都可以访问到 14 fn1.say() // hello world 15 //一般情况下,我们都会自定义函数的prototype属性,但是这里需要注意如果是直接通过 fn.prototype = {} 来定义的话,会切断已经创建的实例与该 prototype 的联系,之前创建的实例的prototype还是指向旧的那个prototype 16 function foo() { } 17 foo.prototype.test = function() { 18 console.log(‘foo.test‘) 19 } 20 var fn1 = new foo() 21 fn1.test() 22 // 此时重置了foo.prototype,指向了另外一个对象,所以会切断fn1跟下面的test1的联系 23 foo.prototype = { 24 test1: function() { 25 console.log(‘foo.test1‘) 26 } 27 } 28 fn1.test() //正常访问 29 fn1.test1() // 报错,因为fn1.prototype 是创建该实例之前的prototype而不是后面重置的prototype 30 var fn2 = new foo() 31 console.log(fn1.test) // undefined,因为新的foo.prototype 没有test属性 32 console.log(fn1.test1) //可以调用,因为fn1指向的是新的foo.prototype 33 // 而且需要注意的一点是,如果直接通过 foo.prototype = {} 重置prototype,那么foo.prototype.constructor 不是指向 foo,如果需要使用 constructor 来做一些逻辑操作,这里需要注意再将 constructor 指向 foo: 34 foo.prototype = { 35 constructor: foo 36 }
这里顺便说一下在Vue里面的一点小技巧,在使用Vue的时候,因为Vue文件里面都是Vue的实例,可以直接访问到vue.prototype对象里面的属性的,所以可以通过 Vue.prototype.$xxx 定义一个Vue全局属性,这样在所有的vue实例都是可以直接访问的。Vue.xxx 是直接挂在Vue上面的,vue实例无法直接访问到,在一些 service 逻辑里面,可以通过 import vue 引入 vue,通过 vue.xxx 也可以访问到,或者如果不想定义那么多属性的话,也可以直接通过 vue.prototype.$xxx 获取
原文:https://www.cnblogs.com/l-c-blog/p/10850563.html