js原型与继承

由于学了java 的继承,而es6前没有class 和extends 导致我几天了都学不懂原型,到我刚才看到了阮一峰的文章似乎有所顿悟。记录一下。
大佬文章:
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

  1. C++和Java使用new命令时,都会调用"类"的构造函数(constructor)。他就做了一个简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。
    loot at this
		let fn = function() {this.a = 1;this.b = 2;}let obj = new fn();
  1. 用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。比如,在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); //trueconsole.log(Person); //f(){this.a = 1;this.b = 2;}</script>

打印实例对象:
在这里插入图片描述
一直找下去
在这里插入图片描述

按上面峰哥所讲,结合自己理解。

  1. 构造函数里面有一个原型对象用来存放一些公有属性。
  2. 而实例对象会存放两个东西,一个是通告构造函数得来得本地属性和方法,一个是__proto__引用指向构造函数里面的原型对象。(不同浏览器也叫它[[Prototype]])
    补充一点应该是3个实例对象还有一个constructor 默认指向了原型的constructor罢了。
  3. 原型是构造函数所有,原型对象有你设置的1.属性或者方法,构造函数指向(也就是构造函数Person()),proto,引用指向原型的对象的原型对象,最终是Object的原型对象,而Object的原型对象再上一级就是null。
  4. 这就是我所了理解的原型链。

终于绕懂一点点,太菜了,接下来就是各种各样的继承实现了。
为啥又几种继承方法呢?
还记得一个javascript的创建方法有几种。
字面量,构造函数,通过object方式创建,es6 class等

  1. 构造函数
    通过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); //动物
  1. 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的写法}
  1. 非构造函数的实现:
    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;}

深浅拷贝原理可以看上一篇

  1. es6 extends 终于来到es6 这里了 哭

标准写法:

class A {}class B extends A {constructor() {super();}
}

注意以下几点就行:

  1. 子类构造器一定得调用super()//java差不多了

这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

  1. 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


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部