什么是原型链?如何使用?

1.背景介绍

Javascript继承机制设计思想

当时JS的诞生主要是解决用户和浏览器无法互动的问题,实现一些简单的功能例如表单验证。作为脚本语言它没有子类父类的概念,但作为面对对象的编程语言它又需要实现继承,所以有了不同与JAVA,C++的继承机制,JS的继承全靠原型链来实现继承。
JS考了C++和Java都是用new生成实例于是便将new引入到了JS,用来从构造函数生成一个实例对象构造函数生成的实例无法共享属性和方法,考虑到这点于是将prototype属性引入到了JS中


2.知识摘要

什么是原型对象

简单来讲原型对象就是拥有公用属性和方法的对象,所有对象一经创建都会自动引用该对象,以此来实现继承。原型对象是构造函数的prototype属性所指的对象。

  • rototype
    该属性是函数才拥有的属性!实例一经创建讲自动引用prototype的属性和方法。prototype属性指向一个对象,这个对象就是我们所说的原型对象
  • __proto__
    该属性是JS中所有对象皆拥有的属性,指向其自身的创建者(构造函数)的prototype

    举个栗子

 function Dog(name) {
     this.name = name;
 }
 var badDog = new Dog("二狗子");
 badDog.__proto__ == Dog.prototype  //true

badDog是构造函数Dog的实例,__proto__指向的是其构造函数的prototype,那么在这个demo当中,badDog.__proto__和Dog.prototype所指的是同一个原型对象。构造函数,实例,原型对象他们的三者关系如下
这里写图片描述

什么是原型链?

控制台输入badDog.vlaueOf(),结果输出Dog {name: “二狗子”}
我们可以思考一下这个vlaueOf()是哪来的?
实例和构造函数皆没有定义这个方法,方法也不会凭空生出来,这个方法就是通过它的原型对象继承来的,它的原型对象还有一个原型对象,直到找到vlaueOf()这个方法为止,这个查询的链条就是原型链,JS就是通过这个链条来实现的继承。
这个实例的__proto__指向Dod.prototype,查询结果没有改方法,它会继续像下找Dog.porototype.proto,该层指向的是Dog原型对象的构造函数的原型对象,所有对象都是构造函数Object的实例,那么就是指Object.prototype,在这里找到了vlaueOf方法,返回。


3.常见问题

如何访问原型对象?


4.解决方案

prototype,只有函数对象才拥有一个属性,用来访问其原型对象,可以读写


__proto__,是任何引用类型都拥有的属性,指向其构造函数的prototype,但是并不是标准写法,是一个特殊的内部属性,不过目前所有浏览器都支持该属性,可以读写
Object.getPrototypeOf()该属性是ES5引入的一个标准属性,用来获取对象的原型,其作用和 proto是一样的

5.编码实战

 Dog.prototype.like="money";
 badDog.__proto__.hate="money";
 Object.getPrototypeOf(badDog).languge="Wang";
 console.log(badDog.like);   //money
 console.log(badDog.hate);   //money
 console.log(badDog.languge);    //Wang

使用三个方法分别设置badDog的原型对象,控制台打印可以看到结果,都没有问题
这里要注意的是尽量不要写__proto__,使用标准写法Object.getPrototypeOf


6.扩展思考

使用prototype需要注意什么?
使用prototype时尽量去增添,使用对象字面量赋值有风险
举例

 function Dog(name) {
     this.name = name;
 }
 var badDog = new Dog("二狗子");

 Dog.prototype.like="money";

 console.log(badDog.like)  //“money”

以上是增添的写法,在原有的原型上添加属性
下面的例子是使用字面量覆盖

 function Dog(name) {
     this.name = name;
 }
 Dog.prototype={
     like:"money"
 };
 var badDog = new Dog("二狗子");


 console.log(badDog.like) //“money”

该例子运行顺序解读
声明构造函数
将Dog的prototype指针指向新的对象
声明一个Dog的实例,同时将实例的__proto__指针指向Dog.prototype
badDog没有like属性,但是它的__proto__有,输出值”money”,正常。

but
将上面例子改一下,将创建实例放在前面来

 function Dog(name) {
     this.name = name;
 }

 var badDog = new Dog("二狗子");
 Dog.prototype={
     like:"money"
 };

 console.log(badDog.like)   //undefined

调一下位置就会发现输出未定义。。
解释一下运行过程
1.首先生成构造函数Dog,同时Dog.prototype指向一个空的原型对象,这里暂且标记这个对象为a,当然这个对象a还有个__proto__指针。
2.然后生成实例badDog,同时badDog的原型对象指向a,此时,Dog和badDog共享同一个原型对象
3.重点来了—此时生成新的对象{like:”money”},暂且称它为b,然后将Dog.prototype指向b
↑此时的情况是,badDog的__proto__指向a,Dog.prototype指向b,所以badDog.like输出未定义,只是该了下书写位置,整个过程就不一样了,所以这种写法 不可取。


7.参考文献

https://zhuanlan.zhihu.com/p/35790971
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html


8.更多讨论

Q1:为什么推荐使用增添的方法修改原型链
A1:使用对象覆盖的话可能会因为引用类型的问题出现bug,虽然原因很好理解也好解决,但是难免会疏忽,但增添的方法就不会,因为它总是在同一个对象上做修改,并没有动指针。
Q2:使用Object.getprototype()有没有要注意的,他的支持性如何
A2:后面的参数必须是对象,否则会抛出异常。该方法是es5中添加的,在ie9以上都是支持的
Q3:继承还有哪几种方法?
A3:本篇主要讲了原型链继承,他的特点是总是共享的,还提到了构造函数继承,通过给构造函数添加方法那么它的实例也会继承。其他的方法还有call()方法和apply()方法,这个可以通过w3c找到相关语法。还有一种是混合继承,使用构造函数并非最好的办法,使用原型链就无法使用带参数的构造函数了,所以比较好的解决方案是两者都用上,这点在http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp有详细的介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值