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); //true
        console.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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值