blog's People
blog's Issues
Js 原型链剖析
JS 原型链剖析
原型链是JS的核心语言特性之一。本文要解决的问题有以下几点:
- 原型链是为了解决什么问题而出现的?
- 原型链有哪些特性?
- 如何用原型链实现继承?
- 原型链存在哪些问题?(挖坑)
原型链简介
prototype 是为解决js继承问题而提出的一个概念。定义在 prototype 上的属性和方法可以被该类的所有实例共享。
子类的 prototype 指向其父类;原型链的顶端是Object,Object 的 prototype 是 null。
proto 和 prototype
[[prototype]] 是 constructor 的内部属性,用于生成 proto 。如果 prototype 是鸡, proto 就是她下的蛋。当顺着原型链查找属性和方法时其实是从 proto 这条链上查找的。proto 是一个用来访问内部属性 [[prototype]]的 acessor。
function Foo(name) {
this.name = name;
}
const f = new Foo('b');
f.__proto__ === Foo.prototype
如上
- prototype 是构造函数Foo的属性,实例 f 可以通过 proto 来访问。
- 构造函数的 proto 是Function,Function 的 proto 是Object。
- 构造函数的 prototype 指向他自己,是一个循环引用。
原型链的作用
我们可以通过 prototype 高效地创建类和实现继承。
js 创建类的几种方式
创建对象有多种模式,工厂模式,构造函数模式,原型模式
工厂模式
function createPerson(name,age){
const obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName=function(){
return name;
}
return obj;
}
可以看到,工厂模式创建出来的对象无法判断判断其类型;而且方法不能共享,造成浪费。
构造函数模式
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
return this.name;
}
}
通过构造函数模式创建出的对象可以判断类型了,然而方法还是不能共享。
原型模式
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayName= function(){
return this.name;
}
可以看到,通过 prototype 创建的对象既能判断类型,也实现了方法的共享。完美解决了上面两种模式存在的问题。
js实现继承的几种方式
prototype 除了能高效地创建类,也能帮我们实现继承。下面介绍了通过 prototype 实现继承的几种方式。
原型链继承
因为定义在 prototype 上的属性和方法可以被所有实例共享,所以可以通过将父类的实例赋值给子类的 prototype 实现继承。即:
子类的 protoType = new 父类() ,举例:
function Super(){
this.relation = ['friends','teacher']
}
Super.prototype.saySuperName = function(){
return this.relation;
}
function Sub(name){
this.name = name;
}
Sub.prototype = new Super();
Sub.prototype.saySubName = function(){
return this.subName;
}
const s = new Sub('sub1');
s.saySuperName(); //super
s.relation; //['friends','teacher']
s.relation.push('student')
const s1 = new Sub('sub2');
s1.relation; //['friends','teacher','student']
可以看到,通过上述方式实现继承有两个严重的问题:如果父类的属性是引用类型的,那么因为所有子类的实例都共用了父类的同一个实例,所以都共用该引用类型的属性,他们其中一个修改了该引用类型的属性,其他实例的该属性都会跟着修改。
借用构造函数
为了解决上述问题,可以在子类的构造函数里面调一下父类的构造函数 Super.call(this,pra1,par2...) ,这样相当于每次创建一个子类的实例时,都复制了一个新的属性给实例。如下:
function Super(relation){
this.relation = relation;
}
Super.prototype.saySuperName = function(){
return this.relation;
}
function Sub(name,parentRelation){
Super.call(this,parentRelation);
this.name = name;
}
Sub.prototype = new Super();
Sub.prototype.saySubName = function(){
return this.subName;
}
const s = new Sub('sub1',['friends','parents']);
s.saySuperName(); //super
s.relation; //['friends','teacher']
s.relation.push('student')
const s1 = new Sub('sub2',['child']);
s1.relation; //['child']
这种方式的问题是:会调用两次父类的构造函数,造成了浪费。一次是在创建子类的时候,一次是在 子类的 protoType = new 父类() 的时候。
道格拉斯方法
子类的 prototype = object(父类.prototype)
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function Super(relation){
this.superRelation = ['super0','super1','super2'];
this.relation = relation;
}
Super.prototype.saySuperName = function(){
return this.relation;
}
function Sub(name,parentRelation){
Super.call(this,parentRelation);
this.name = name;
}
Sub.prototype = object(Super.prototype);
Sub.prototype.saySubName = function(){
return this.subName;
}
const s = new Sub('sub1',['friends','parents']);
s.saySuperName(); //super
s.relation; //['friends','teacher']
s.relation.push('student')
const s1 = new Sub('sub2',['child']);
s1.relation; //['child']
小疑问:Sub.prototype = object(Super.prototype); 这里为啥不直接写成 Sub.prototype = Super.prototype; 呢?因为会破坏父子之间的层级关系。
参考资料
- http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
- 《js 高级程序设计》第6章-面向对象的程序设计
Blog
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.