js是一门动态语言,js没有类的概念,ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型。

至于继承,js的继承与java这种传统的继承不一样.js是基于原型链的继承.

  • 在javascript里面,每个对象都有一个prototype属性,指向它的原型对象.这个原型对象里面同时还有自己的原型,原型一环扣一环,直到某个对象的原型为null,这一级一级的链结构就是原型结构.

js的原型链继承

  • 继承属性

js属性查找:由于js原型链的存在,当查找一个对象属性时候,不只是在对象上查找,还会沿着该js对象的原型链往上查找,知道找到一个匹配的属性或者查找到原型链末尾.当然如果js对象上与其原型的对象上都有同名的属性,我们遵循该属性的作用域就近原则(术语叫做"属性遮蔽").

var Ele = function(){
    this.a = 1;
    this.b = 2;
     }
    var Obj = function(){
    this.c = 3;
    this.b = 4;
     }
     Ele.prototype = new Obj();
    var ele1 = new Ele(); 
    console.log(ele1.a);//1
    console.log(ele1.b);// 2
    console.log(ele1.c);// 3
    console.log(ele1.d);//undefined
  • 继承方法

在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性/方法。函数的继承与其他的属性继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言(java)的方法重写)。

var student= {
    age : 20,
    name: 'cp',
    sayhi : function(){
        console.log('hi');
    }
     }
     var s1 = Object.create(student); //一种创建对象的方法,后面会写博客介绍.s1.prototype是student
       s1.name = 'kobe';
     console.log( s1.name ); // 'kobe'
     console.log( s1.age ); // 20
    console.log( s1.sayhi() ); // hi

多种方法创建对象及生产原型链

  • 普通对象字面量创建对象

   var o = {
    a: 1
     };
     console.log(o.hasOwnProperty('a')); //true
    // o这个对象继承了Object.prototype上面的所有属性
         // 所以可以这样使用 o.hasOwnProperty('a').判断一个对象是否还有一个属性
     // hasOwnProperty 是Object.prototype的自身属性。
      // Object.prototype的原型为null。
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组的实例对象都继承于Array.prototype 
// (indexOf, forEach等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
  return 2;
}
    // 函数都继承于Function.prototype
    // (call, bind等方法都是从它继承而来):
    // f ---> Function.prototype ---> Object.prototype ---> null

- 使用构造函数(构造器)创建对象

    `function Student() {
     this.job = '读书';
     this.tag = '年轻';
}
function BoyStudent() {
  this.sex = 'boy';
}
BoyStudent.prototype = new Student();
var xiaoMing = new BoyStudent();
console.log(xiaoMing.sex); //boy
console.log(xiaoMing.tag); //年轻
// xiaoMing是生成的对象,他的自身属性有'sex'.
// 在BoyStudent被实例化时,BoyStudent.[[Prototype]]指向了Student.prototype.
  • 使用Object.create()创建对象,新对象的原型就是调用 create 方法时传入的第一个参数:

var a = {a: 1}; 
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
  • 使用 class 关键字

ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不一样的。 JavaScript 仍然是基于原型的,这点一直不变。这些新的关键字包括 class, constructor, static, extends, 和 super.(跟java的关键字一样,构造器,静态,继承,超类)

"use strict";
     //类 Person
     class Person(){
    constructor(name , age){
      this.name = name;
        this.age = age;
    };
    }
    // 类Girl继承Person
      class Girl extends Person(){    
    constructor(name , age){
        super(name , age);
    }
    getinfo(){
        return this.name + ',' + this.age;
    }
     }
    var girl = new Girl('kobe',20);
  • 原型继承的性能

    1. 在原型链上查找属性比较耗时,对性能有副作用,尽量避免,这在性能要求苛刻的情况下很重要。另外,访问不存在的属性时会遍历整个原型链,浪费资源。

    2. 遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。

    3. 检测对象的属性是定义在自身上还是在原型链上,有必要使用 hasOwnProperty 方法,所有继承自 Object.proptotype 的对象都包含这个方法,返回布尔值,是 JavaScript 中唯一一个只涉及对象自身属性而不会遍历原型链的方法。

    注意:仅仅通过判断值是否为 undefined 还不足以检测一个属性是否存在,一个属性可能存在而其值恰好为 undefined。

  • 关于原生对象的原型扩展

理论上我们不应该去扩展Object.prototype,或者其他内置对象的原型,像Array.prototype等。

我们去扩展内置对象原型的唯一理由是引入新的 JavaScript 引擎的某些新特性,比如 Array.forEach。

  • 理解prototype与Object.getPrototypeOf区别

function A(a){
    this.varA = a;
    }
    // 以上函数 A 的定义中,既然 A.prototype.varA 总是会被 this.varA 遮蔽,
// 那么将 varA 加入到原型(prototype)中的目的是什么?
A.prototype = {
  varA : null,  // 既然它没有任何作用,干嘛不将 varA 从原型(prototype)去掉?
      // 也许作为一种在隐藏类中优化分配空间的考虑?
      // https://developers.google.com/speed/articles/optimizing-javascript#Initializing instance variables
      // 将会验证如果 varA 在每个实例不被特别初始化会是什么情况。
  doSomething : function(){
    // ...
  }
}

当你 var a1 = new A();js内部就会设置a1.[[prototype]] == A.prototype .

如果你再 var a2 = new A();那么 a1.doSomething 事实上会指向Object.getPrototypeOf(a1).doSomething,

它就是你在 A.prototype.doSomething 中定义的内容。

比如:Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething。

prototype 是用于类型的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。

var Fx = function(){};
    var fx = new Fx();

相当于

var fx = new Object();
    fx[[prototype]] = Fx.prototype;
    Fx.call(fx);

在用原型继承编写复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。此外,除非为了兼容新 JavaScript 特性,否则,永远不要扩展原生的对象原型。


xyzCoding
38 声望8 粉丝

前端开发