ES5 的构造函数原型链继承

更新日期: 2019-10-04阅读: 2k标签: 继承

构造函数

构造函数,就是专门用来生成实例对象的函数。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。

function Person(name){
    this.name = name;
}

为了与普通函数区别,构造函数名字的第一个字母通常大写。

构造函数的特点有两个:

  • 函数体内部使用了 this 关键字,代表了所要生成的对象实例。
  • 生成对象的时候,必须使用 new 命令。


new 命令

基本用法

new 命令的作用,就是执行构造函数,返回一个实例对象。

let a = new Person('dora');
a.name      // dora

new 命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,但为了表明是函数调用,推荐使用括号表示更明确的语义。

new 命令的原理

使用 new 命令时,它后面的函数依次执行下面的步骤:

  1. 创建一个空对象,作为将要返回的对象实例。let o = new Object();
  2. 将这个空对象的原型,指向构造函数的prototype属性。 Object.setPrototypeOf(o,Foo.prototype);
  3. 将构造函数的 this 绑定到新创建的空对象上。Foo.call(o);
  4. 始执行构造函数内部的代码

如果构造函数内部有 return 语句,而且后面跟着一个对象,则 new 命令会返回 return 语句指定的对象;否则,就会不管 return 语句,返回 this 对象,且不会执行 return 后面的语句。

function Person(name) {
  this.name = name;
  if (name == undefined) {
    return {};
  }else if(typeof name != 'string'){
    return '姓名有误';
  }
  console.log(111);
}

new Person();        //  {}
new Person(123);     //  {name: 123}

new Person('dora');  
// {name:'dora'}
// 111

new.target

如果当前函数是 new 命令调用的,在函数内部的 new.target 属性指向当前函数,否则为 undefined。

function F() {
  console.log(new.target === F);
}

F()       // false
new F()   // true

使用这个属性,可以判断函数调用时,是否使用了 new 命令。

function F() {
  if (!new.target) {
    throw new Error('请使用 new 命令调用!');
  }
}

F()       // false
new F()   // true

强制使用 new 命令

如果不使用 new 命令执行构造函数就会引发一些意想不到的结果,所以为了保证构造函数必须与 new 命令一起使用,除了 new.target 之外也可以有以下两个解决办法。

1. 构造函数内部使用严格模式

在构造函数内部第一行加上 use strict。这样的话,一旦忘了使用 new 命令,直接调用构造函数就会报错。

function Person(name, age){
  'use strict';
  this.name = name;
  this.age = age;
}

Person()
//  TypeError: Cannot set property 'name' of undefined

报错原因是因为不加 new 调用构造函数时,this 指向全局对象,而严格模式下,this 不能指向全局对象,默认等于 undefined,给 undefined 添加属性肯定会报错。

2. 在构造函数内部通过 instanceof 判断是否使用 new 命令

function Person(name, age) {
  if (!(this instanceof Person)) {
    return new Person(name, age);
  }

  this.name = name;
  this.age = age;
}

Person('dora', 18).name          // dora
(new Person('dora', 18)).age     // 18

在构造函数内部判断 this 是否是构造函数的实例,如果不是,则直接返回一个实例对象。


prototype 原型

任何函数都有一个 prototype 属性,这个属性称为函数的“原型”,属性值是一个对象。只有函数有原型属性

function f(){}
typeof f.prototype   // "object"

对于普通函数来说,原型没有什么用。但对于构造函数来说,通过 new 生成实例的时候,该属性会自动成为实例对象的原型对象。

prototype 属性的作用

用来定义所有实例对象共用的属性和方法。

如果将对象的方法写入构造函数中,则 new 多少个实例,方法将会被复制多少次,虽然复制出来的函数是一样的,但分别指向不同的引用地址,不利于函数的复用。

function Person(){
  this.name = function(){
      console.log('dora');
  }
}
let p1 = new Person();
let p2 = new Person();

p1.name === p2.name     // false

因此,将所有的属性都定义在构造函数里,所有的方法都定义在构造函数的原型中。这样,实例的方法都指向同一个引用地址,内存消耗小很多。

constructor 属性

函数原型 prototype 对象的属性,指向这个原型所在的构造函数,可以被所有实例对象继承,指向构造自己的构造函数。

Person.prototype.constructor.name   //  "Person"
p1.constructor.name                 //  "Person"

函数的 name 属性返回函数名。

给 prototype 添加属性

1. 点语法追加属性

Person.prototype.sayHi = function(){
    console.log('Hi,I am Dora');
}

2. 覆盖原型对象

Person.prototype = {
  sayHi: function(){
      console.log('Hi,I am Dora');
  }
}

这样用对象字面量直接覆盖,会让 constructor 与构造函数失联,可以手动补上这个属性。

Person.prototype = {
  constructor: Person,
  sayHi: function(){
      console.log('Hi,I am Dora');
  }
}

定义构造函数原型中的方法时尽量不要相互嵌套,各方法最好相互独立。


_proto_ 原型对象

任何一个对象都有 __proto__ 属性,这个属性称为对象的“原型对象”,一个对象的原型对象就是它的构造函数的 prototype。

function Person(){}
let p1 = new Person();

p1.__proto__ === Person.prototype  // true

Object.getPrototypeOf(obj)

__proto__ 并不是语言本身的属性,这是各大浏览器厂商添加的私有属性,虽然目前很多浏览器都可以识别这个属性,但依旧不建议在生产环境下使用,避免对环境产生依赖。

生产环境下,我们可以使用 Object.getPrototypeOf(obj) 方法来获取参数对象的原型。

Object.getPrototypeOf(p1) === Person.prototype // true


原型链机制

当访问对象的属性时,如果这个对象没有这个属性,系统就会查找这个对象的 __proto__ 原型对象,原型对象也是个对象,也有自己的 __proto__ 原型对象,然后就会按照这个原型链依次往上查找,直到原型链的终点 Object.prototype。

Object() 是系统内置的构造函数,用来创建对象的, Object.prototype 是所有对象的原型链顶端,而Object.prototype的原型对象是 null。

Object.getPrototypeOf(Object.prototype) // null

如果对象自身和它的原型都定义了一个同名属性,那么优先读取对象自身的属性。


继承

继承的核心是 子类构造函数的原型是父类构造函数的一个实例对象。

首先继承父类的属性

// 1. 父类构造函数
function Super(data){
  this.data = data;
};
Super.prototype.funName = function(){};

// 2. 子类构造函数
function Sub(){
  // 用来继承父类的参数和属性
  Super.apply(this, arguments);
}

其次继承父类的方法

  1. 整体继承

    Sub.prototype = Object.create(Super.prototype);
    
    // or
    
    Sub.prototype = new Super();
  2. 单个方法的继承

    Sub.prototype.funName = function(){
      Super.prototype.funName.call(this);
      // some other code
    }

最后需要改变 constructor 指向

此时子类实例的 constructor 指向父类构造函数 Super,需手动改变。

Sub.prototype.constructor = Sub;

多重继承

ES5 没有多重继承功能,即不允许一个对象同时继承多个对象,但可通过变通方法实现这个功能。

function S(){
  M1.call(this);
  M2.call(this);
};
S.prototype = Object.create(M1.prototype); // 继承 M1
Object.assign(S.prototype,M2.prototype);   // 继承链上加入 M2
S.prototype.constructor = S;               // 指定构造函数。


实例验证

instanceof 运算符

instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例。继承的子类实例也是父类的实例,因此继承的也为 true。

let d = new Date();
d instanceof Date    // true
d instanceof Object  // true

Object.prototype.isPrototypeOf()

实例对象可继承 isProtorypeOf()方法,用来判断该对象是否为参数对象的原型对象。

只要实例对象处在参数对象的原型链上,isPrototypeOf() 方法都返回 true。

let o1 = {};
let o2 = Object.create(o1);
let o3 = Object.create(o2);

o2.isPrototypeOf(o3)   // true
o1.isPrototypeOf(o3)   // true
o2.isPrototypeOf(o2)   // false

链接: https://www.fly63.com/article/detial/5798

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

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

各种实现js继承的方法总结

学过java的同学应该都知道,继承是java的重要特点之一,许多面向对象的语言都支持两种继承方式:接口继承和实现继承,接口继承只继承方法签名,而实现继承则继承实际的方法,在js中,由于函数没有签名,因此支持实现继承,而实现继承主要是依靠原型链来实现的,那么,什么是原型链呢?

css的继承性

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

js原型继承的几种方式

1. 原型链继承;2,构造函数继承(对象冒充继承);3,组合继承(原型链继承+构造函数继承);4,原型式继承;5. 寄生组合式继承,为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术

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

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

Js常用的继承方式

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

JS之继承(ES5 & ES6)

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

Js继承总结

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。如果让原型对象指向另一个类型的实例.....有趣的事情便发生了.

Js对象继承

JavaScript对象继承的方法有很多,这里总结一下几种比较常用的方法。使用call/apply和Object.create()第一种方法使用call或apply方法,改变了 this 的指向而实现继承,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行

Js实现继承的几种方法及其优缺点

要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链;每一个构造函数都有prototype属性(显示原型),用来显示修改对象的原型

点击更多...

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