资讯专栏INFORMATION COLUMN

理解js的原型与继承

wthee / 2620人阅读

摘要:相当于在用原型继承编写复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。

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);

原型继承的性能

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

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

检测对象的属性是定义在自身上还是在原型链上,有必要使用 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 特性,否则,永远不要扩展原生的对象原型。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80968.html

相关文章

  • 你是否理解jsObjectFunction原型

    摘要:原型对象是由创建的,因此原型对象的构造函数是构造函数也可以是称为对象,原型对象也就继承了其生父构造函数中的数据,也同时继承了原型对象的数据。当然这条原型链中的数据,会被还是还是这类构造函数继承,但是不会被这些继承,他们不处于同一个链条上。 js中,Function的本质是什么?Object的本质又是什么?js中有几条原型链? showImg(https://segmentfault.c...

    itvincent 评论0 收藏0
  • 面向对象 JavaScript

    摘要:是完全的面向对象语言,它们通过类的形式组织函数和变量,使之不能脱离对象存在。而在基于原型的面向对象方式中,对象则是依靠构造器利用原型构造出来的。 JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或者只是部分具备一些面向对象的特征。本文将回归面向对象本意,从对语言感悟的角度阐述为什...

    novo 评论0 收藏0
  • js继承理解

    摘要:创建自定义的构造函数之后,其原型对象只会取得属性,其他方法都是从继承来的。优缺点寄生式继承在主要考虑对象而不是创建自定义类型和构造函数时,是十分有用的。 原文链接:https://kongchenglc.coding.me... 1.原型链   js的继承机制不同于传统的面向对象语言,采用原型链实现继承,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。理解原型链必须先理...

    BlackFlagBin 评论0 收藏0
  • 深入理解 js继承原型

    摘要:原型链与继承当谈到继承时,只有一种结构对象。如果对该图不怎么理解,不要着急,继续往下看基于原型链的继承对象是动态的属性包指其自己的属性。当使用操作符来作用这个函数时,它就可以被称为构造方法构造函数。 原型链与继承 当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object )都有一个私有属性(称之为proto)指向它的原型对象(prototype)。该原型对象也...

    xingqiba 评论0 收藏0
  • js对象详解(JavaScript对象深度剖析,深度理解js对象)

    摘要:对象详解对象深度剖析,深度理解对象这算是酝酿很久的一篇文章了。用空构造函数设置类名每个对象都共享相同属性每个对象共享一个方法版本,省内存。 js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了。 JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕。 平时发的文章基本都是开发中遇到的问题和对...

    CatalpaFlat 评论0 收藏0

发表评论

0条评论

wthee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<