由于学了java 的继承,而es6前没有class 和extends 导致我几天了都学不懂原型,到我刚才看到了阮一峰的文章似乎有所顿悟。记录一下。
大佬文章:
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
- C++和Java使用new命令时,都会调用"类"的构造函数(constructor)。他就做了一个简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。
loot at this
let fn = function() {
this.a = 1;
this.b = 2;
}
let obj = new fn();
- 用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。比如,在DOG对象的构造函数中,设置一个实例对象的共有属性species。
function DOG(name){
this.name = name;
this.species = '犬科';
}
//然后,生成两个实例对象:
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
//这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。
dogA.species = '猫科';
alert(dogB.species); // 显示"犬科",不受dogA的影响
考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性。
这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
function DOG(name){
this.name = name;
}
DOG.prototype = { species : '犬科' };
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
alert(dogA.species); // 犬科
alert(dogB.species); // 犬科
//现在,species属性放在prototype对象里,是两个实例对象共享的。
//只要修改了prototype对象,就会同时影响到两个实例对象。
DOG.prototype.species = '猫科';
alert(dogA.species); // 猫科
alert(dogB.species); // 猫科
以上是峰哥的代码和思考。
现在让人恶心的地方来了
构造函数,prototype,_proto_,实例对象 ,
把这几个的关系弄懂 原型链也就懂了。
<script>
//prototype:原型;雏形;最初形态
let Person = function() {
this.a = 1;
this.b = 2;
}
let person1 = new Person();
Person.prototype.age = 21;
console.log(person1);
console.log(person1.constructor); //f(){this.a = 1;this.b = 2;}
console.log(Person.prototype); //{constructor:f}
console.log(person1.__proto__); //{constructor:f}
console.log(person1.__proto__ === Person.prototype); //true
console.log(Person); //f(){this.a = 1;this.b = 2;}
</script>
打印实例对象:
一直找下去
按上面峰哥所讲,结合自己理解。
- 构造函数里面有一个原型对象用来存放一些公有属性。
- 而实例对象会存放两个东西,一个是通告构造函数得来得本地属性和方法,一个是__proto__引用指向构造函数里面的原型对象。(不同浏览器也叫它[[Prototype]])
补充一点应该是3个实例对象还有一个constructor 默认指向了原型的constructor罢了。 - 原型是构造函数所有,原型对象有你设置的1.属性或者方法,构造函数指向(也就是构造函数Person()),proto,引用指向原型的对象的原型对象,最终是Object的原型对象,而Object的原型对象再上一级就是null。
- 这就是我所了理解的原型链。
终于绕懂一点点,太菜了,接下来就是各种各样的继承实现了。
为啥又几种继承方法呢?
还记得一个javascript的创建方法有几种。
字面量,构造函数,通过object方式创建,es6 class等
- 构造函数
通过apply改变Animal的构造函数this指向。
function Animal() {
this.species = "动物";
}
function Cat(name, color) {
Animal.apply(this, arguments)
this.name = name;
this.color = color;
}
let cat1 = new Cat('大花', '黑色');
console.log(cat1.species); //动物
- prototype:
2.1 子原型指向父的实例对象
function Animal() {
this.species = "动物";
}
function Dog(name, color) {
this.name = name;
this.color = color;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog; //如果没有这行的话Dog的原型对象没有构造函数
let dog = new Dog("大旺", 'white')
console.log(dog);
为啥需要这个:Dog.prototype.constructor = Dog;
原型对象有constructor 指向构造函数
而实例对象也有一个constructor指向原型对象的constructor ,如果不写可能导致原型链混乱。
继续;
2.2 子原型指向父原型
<script>
function Animal() { }
function Dog(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.speices = '动物';
Dog.prototype = Animal.prototype;
Dog.prototype.constructor = Dog;
let dog = new Dog("二毛", "black")
console.log(dog);
console.log(Animal.prototype.constructor);//Dog
</script>
Dog的原型对象指向了Animal ,没有new Animal 对象也就没省了内存,
but,把Animal的原型对象与Dog绑定了。如上最后一行。
2.3 通过空对象,上面两个的结合,即省内存,有不两个原型绑定。
function Animal() {}
function Cat(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.species = "动物"
var F = function() {};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
let cat = new Cat('花花', 'white')
alert(cat.species)
最终版:
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
//开一口 备用,实现继承的完备性,没啥用 yui的写法
}
- 非构造函数的实现:
3.1 object(o)函数
json格式的发明人Douglas Crockford,提出了一个object()函数。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
3.2 深浅拷贝,把属性都复制了那不就是继承了吗,气抖冷》》》》(JQuery使用)
浅:
function extendCopy(o){
let c = {}
Object.keys(o).forEach(key=>{
c[key] = o[key];//es6 懒得写es5了
})
c.uber = o;
return c;
}
function deepCopy(o,c){
let c = c || {}
Object.keys(o).forEach(key=>{
if(typeof o[key] ==="object"){
deepCopy(o[key],c[key]);
}
else{
c[key]=o[key];
}
})
return c;
}
深浅拷贝原理可以看上一篇
- es6 extends 终于来到es6 这里了 哭
标准写法:
class A {}
class B extends A {
constructor() {
super();
}
}
注意以下几点就行:
- 子类构造器一定得调用super()//java差不多了
这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
- es5 6区别
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6
的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
参考:
https://es6.ruanyifeng.com/#docs/class-extends
https://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html
https://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
峰大佬yyds