浅析prototype

在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 获取

猜你喜欢

转载自www.cnblogs.com/l-c-blog/p/10850563.html