Js继承总结

更新日期: 2019-08-28 阅读: 2.7k 标签: 继承

原型链

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
如果让原型对象指向另一个类型的实例.....有趣的事情便发生了.
即: Person.prototype = animal2
鉴于上述游戏规则生效,如果试图引用Person构造的实例person1的某个属性:

1).首先会在instance1内部属性中找一遍;
2).接着会在instance1.__proto__(constructor1.prototype)中找一遍,而constructor1.prototype 实际上是animal2, 也就是说在animal2中寻找该属性p1;
3).如果animal2中还是没有,此时程序不会灰心,它会继续在animal2.__proto__(Animal.prototype)中寻找...直至Object的原型对象

搜索轨迹: person1--> animal2--> Animal.prototype-->Object.prototype

这种搜索的轨迹,形似一条长链, 又因prototype在这个游戏规则中充当链接的作用,于是我们把这种实例与原型的链条称作原型链 .



JavaScript继承

继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
——《你不知道的JavaScript》


基于原型链

不同于其它大部分语言,JavaScript是基于原型的对象系统,而不是基于类。
基于原型的面向对象设计方法总共有三种。

1.拼接继承: 是直接从一个对象拷贝属性到另一个对象的模式。被拷贝的原型通常被称为mixins。ES6为这个模式提供了一个方便的工具Object.assign()。在ES6之前,一般使用Underscore/Lodash提供的.extend(),或者 jquery 中的$.extend(), 来实现。上面那个对象组合的例子,采用的就是拼接继承的方式。

2.原型代理:JavaScript中,一个对象可能包含一个指向原型的引用,该原型被称为代理。如果某个属性不存在于当前对象中,就会查找其代理原型。代理原型本身也会有自己的代理原型。这样就形成了一条原型链,沿着代理链向上查找,直到找到该属性,或者找到根代理Object.prototype为止。原型就是这样,通过使用new关键字来创建实例以及Constructor.prototype前后勾连成一条继承链。当然,也可以使用Object.create()来达到同样的目的,或者把它和拼接继承混用,从而可以把多个原型精简为单一代理,也可以做到在对象实例创建后继续扩展。

3.函数继承:在JavaScript中,任何函数都可以用来创建对象。如果一个函数既不是构造函数,也不是 class,它就被称为工厂函数。函数继承的工作原理是:由工厂函数创建对象,并向该对象直接添加属性,借此来扩展对象(使用拼接继承)。函数继承的概念最先由道格拉斯·克罗克福德提出,不过这种继承方式在JavaScript中却早已有之。


借助构造函数实现继承(经典继承)

 function Parent1() {
   this.name = 'parent1';
 }
 
 Parent1.prototype.say = function () {}
 
 function Child1() {
   Parent1.call(this);
   this.type = 'child';
 }

 console.log(new Child1);


这个主要是借用call 来改变this的指向,通过 call 调用 Parent ,此时 Parent 中的 this 是指 Child1。
有个缺点,从打印结果看出 Child并没有say方法,所以这种只能继承父类的实例属性和方法,不能继承原型属性/方法。
注意 constructor 属性, new 操作为了记录「临时对象是由哪个函数创建的」,所以预先给「Child.prototype」加了一个 constructor 属性:


借助原型链实现继承

function Parent2() {
  this.name = 'parent2';
  this.play = [1, 2, 3];
}

function Child2() {
  this.type = 'child2';
}
Child2.prototype = new Parent2();

console.log(new Child2);


通过一讲的,我们知道要共享莫些属性,需要 对象.__proto__ = 父亲对象的.prototype,但实际上我们是不能直接 操作__proto__,
这时我们可以借用 new 来做,所以Child2.prototype = new Parent2(); <=> Child2.prototype.__proto__ = Parent2.prototype; 这样我们借助 new 这个语法糖,就可以实现原型链继承。
缺点:给 s1.play新增一个值 ,s2 也跟着改了。所以这个是原型链继承的缺点,原因是 s1.__pro__ 和 s2.__pro__指向同一个地址即父类Child2的prototype。


组合继承

是指将原型链和构造函数的相结合,发挥二者之长的一种继承模式。其思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,即通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。


初级版

function Super(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    //继承了Super 属性 (第二次调用Sup构造函数)
    this.age = age;
}

// 继承了Super 原型链上的方法 (第一次调用Sup构造函数) 注意后面需要改造这里,因为我们只想要方法,却生成了属性
Sub.prototype = new Super();    

Sub.prototype.constructor = Sub;// 
Sub.prototype.sayAge = function (){
    alert(this.age);
};

var instance1 = new Sub("Luke", 18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    //18

var instance2 = new Sub("Jack", 20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    //20



优化后的组合继承

function Super(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    //继承了Super 属性
    this.age = age;
}

function F(){
}
F.prototype = Super.prototype; 
Sub.prototype = new F();    // 继承了Super 原型链上的方法

Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function (){
    alert(this.age);
};

var instance1 = new Sub("Luke", 18);
console.log(instance1 )
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    //18

var instance2 = new Sub("Jack", 20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    //20



疑问

为什么要这么写?

function F(){
}
F.prototype = Super.prototype; 
Sub.prototype = new F();    // 继承了Super 原型链上的方法

而不是

Sub.prototype = Super.prototype;

下面的方法没有办法区分一个对象是直接由它的子类实例化还是父类呢?
下面这是第一个方法无法判断

instance1 instanceof Sub//true
instance1 instanceof Super//true

我们还有一个方法判断来判断对象是否是类的实例,那就是用 constructor,我在控制台打印以下内容也无法分辨:


原型继承

借助原型可以基于已有的对象创建新对象, 同时还不必因此创建自定义类型
在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.

function object(o){
    function F(){}
    F.prototype = o;//重写F的原型,将他指向传入的o,这就相当于继承自o
    return new F();//返回F的实例对象
}
var person = {
    friends : ["Van","Louis","Nick"]
};
var anotherPerson = object(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends);//"Van,Louis,Nick,Rob,Style"

从本质上讲, object() 对传入其中的对象执行了一次浅复制.所用的子类都指向传入的person对象

object.create() 方法规范化了上面的原型式继承. 上篇文章有这个方法的详细解释

var person = {
    friends : ["Van","Louis","Nick"]
};
var anotherPerson = Object.create(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends);//"Van,Louis,Nick,Rob,Style"
console.log(anotherPerson)


缺点:

原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数


寄生式继承

核心:在原型式继承的基础上,增强对象,返回构造函数
函数的主要作用是为构造函数新增属性和方法,以增强函数

function createAnother(original){
  var clone = object(original); // 通过调用 object() 函数创建一个新对象,object是一个任何能够返回对象的函数
  clone.sayHi = function(){  // 以某种方式来增强对象
    alert("hi");
  };
  return clone; // 返回这个对象
}

var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

缺点(同原型式继承):
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/5570

相关推荐

css无继承性的属性有哪些?

继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代。CSS继承是指设置上级(父级)的CSS样式,上级(父级)及以下的子级(下级)都具有此属性。但并不是所有的属性都可以继承

Js继承背后的场景-prototype,__proto__, [[prototype]]

prototype是构造函数的一个属性,它决定了在构造出来的对象上__proto__属性将是什么样的。如上图所示,理解JavaScript中的继承的关键是要理解母鸡如何产蛋的过程。

js原型继承、构造函数继承、组合继承法

原型继承缺点:1、不能由子对象像父对象传递参数,2、对于引用型的属性修改之后会印象其他的实例对象;构造函数继承缺点:1、不能继承父对象原型上的方法 2、每次实例化对象会重新构建函数,浪费内存。

CSS inherit是继承,那initial和unset呢?

所有CSS都支持几个全局属性值,分别是: inherit , initial 和 unset 。其中 inherit 是继承的意思,IE8(标准版)开始支持,不是从IE9开始支持的,网上一些文档是错误的。

纯手写实现js继承

继承在前端逻辑操作中是比较常见的,今天我们就从零开始写一个js的继承方式,在es5中继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上Parent.call(this)

Js常用的继承方式

JavaScript常用继承方式主要分为(7种):原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承以及继承多个对象。原型链继承(核心:将父类的实例作为子类的原型

css的继承性

在面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法。那么我们现在主要研究css,css就是在设置属性的。不会牵扯到方法的层面。

JS之继承(ES5 & ES6)

继承到底是什么?继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”。继承可以使得子类具有父类别的各种属性和方法

ES5 的构造函数原型链继承

构造函数,就是专门用来生成实例对象的函数。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。为了与普通函数区别,构造函数名字的第一个字母通常大写。

JS中8种实现继承的方式

可以使得子类别具有父类别的各种属性和方法,而不需要编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。同时也能添加新的属性和方法。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!