JavaScript 原型与继承

JavaScript 中函数原型是实现继承的基础。prototype、construct、原型链以及基于原型链的继承是面向对象的重要内容

prototype

  1. 原型即 prototype,是函数的一个属性,是一个对象

    function Car() {}
    console.log(typeof Car.prototype);
    console.log(Car.prototype);
    // object
    // {...}
  2. 所有被构造函数构造出的对象都能访问 prototype 上定义的属性和方法

    function Car() {
    this.brand = "BMW";
    this.color = "red";
    }
    Car.prototype.price = "100 万";
    var car1 = new Car();
    var car2 = new Car();
    console.log(car1.price);
    console.log(car2.price);
    // 100 万
    // 100 万
  3. 构造函数内部和 prototype 定义了同名属性,实例对象会优先调用构造函数中的属性

    function Car() {
    this.price = "10 万";
    }
    Car.prototype.price = "100 万";
    var car1 = new Car();
    console.log(car1.price);
    // 10 万
  4. 通过实例对象不能更改 prototype 上的属性

    function Car() {}
    Car.prototype.price = "100 万";
    var car1 = new Car();
    car1.price = "10 万";
    console.log(Car.prototype.price);
    // 100 万

一般将不变化的内容或方法放在 prototype 下,需要动态变化的放在构造方法内,通过参数配置

constructor

  1. constructor 指向构造函数本身

    实例对象的 constructor 属性指向构造函数

    function Car() {}
    var car = new Car();
    console.log(car.constructor);
    console.log(Car)
    // Car(){}
    // Car(){}
  2. constructor 可以被更改

    constructor 可以被修改,但是并不会影响实例化对象

    function Bike() {
    this.name = "bike";
    }
    Bike.prototype.name = "Bike";
    function Car() {}
    Car.prototype = {
    constructor: Bike
    }
    var car = new Car();
    console.log(Car.prototype);
    console.log(car.name);
    // {constructor: Bike(){}, ...}
    // undefined

__proto__

  1. 构造函数在实例化时,将其 prototype 挂载到函数内 this 的 __proto__

    function Car() {}
    Car.prototype.name = "Jett";
    var car = new Car();
    console.log(Car.prototype);
    console.log(car.__proto__);
    // Car.prototype ->
    // {
    // name: "Jett",
    // construct: Car(){}
    // _proto_: {...}
    // }
    // car._proto_ ->
    // {
    // name: "Jett",
    // construct: Car(){}
    // _proto_: {...}
    // }
    //

    可以看到,打印出的 Car.prototype 和 car.__proto__ 内容一致。因为在实例化对象时,Car.prototype 被挂载到函数内的 this.__proto__ 上,即实例对象的 __proto__ 属性上

    prototype 是构造函数的属性,__proto__ 属于每个实例对象的,是一个内部属性,它们指向相同的内容

  2. 可以通过实例对象访问 __proto__ 属性,并对其进行修改

    function Car() {}
    Car.prototype.name = 'BWM';
    var car = new Car();
    console.log(car.name);
    car.__proto__= {
    name:"Benz"
    }
    console.log(car.name);
    // BWM
    // Benz

    也可以更改 prototype 的属性到达效果

    function Car() {}
    Car.prototype.name = 'BWM';
    var car = new Car();
    console.log(car.name);
    Car.prototype.name = 'Benz';
    console.log(car.name);
    // BWM
    // Benz

    但是,将 prototype 重新赋值并不能对之前实例化的对象造成影响

    function Car() {}
    Car.prototype.name = 'BWM';
    var car = new Car();
    console.log(car.name);
    Car.prototype = {
    name: "Benz"
    }
    console.log(car.name);
    // BWM
    // BWM

    这是因为重新赋值相当于创建新对象,使 prototype 指向的新的对象,而实例对象的 __proto__ 属性依然指向原来的内容,相当于一个对象的两个引用,其中一个不在指向该对象,而且指向了新对象

    这不能对已经实例化出的对象造成影响,但是后面再实例化对象则可以造成影响,因为实例化过程中将修改后的 prototype 挂载到了实例对象的 __proto__ 属性下,二者指向同一对象

原型链

  1. prototype 中的 __proto__ 属性

    function Car() {}
    var car = new Car();
    console.log(Car.prototype);

    当我们打印构造函数的 prototype 属性时,可以看到

    {
    constructor: Car(),
    __proto__: {...}
    }

    prototype 中也有 __proto__ 属性,实例化过程 protorype 被挂载到实例对象的 __proto__ 下,这就意味着实例对象的 __proto__ 中也有一个 __proto__ 属性

    因为这里的 prototype 是一个非空对象,是由 new Object() 或者其他自定义构造方法实例化出的,自然也有 __proto__ 属性

  2. 链式的 __proto__

    原型链是由 __proto__ 组成的链接,原型链的顶端是 Object.prototype

    JuniorCoder.prototype.basicSkill = "html/css";
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    }
    var junior = new JuniorCoder();
    SeniorCoder.prototype = junior;
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    var senior = new SeniorCoder();
    console.log(senior);

    这里将 JuniorCoder() 的实例对象赋值给 SeniorCoder.prototype,打印出

    SeniorCoder {
    advcedSkill: "vue",
    __proto__: { // senior.__proto__ ,即 SeniorCoder.protoype
    lowerSkill: "javascript",
    __proto__: { // junior.__proto__ ,即 JuniorCoder.prototype
    basicSkill: "html/css",
    __proto__: { // Object.prototype
    constructor: Object(),
    toString: toString()
    // ...
    }
    }
    }
    }

    可以看出,senior 的 __proto__ 属性指向 JuniorCoder() 实例 junior,这是因为之前 将 junior 赋值给了 SeniorCoder.prototype

    此外,junior 的 __proto__ 也指向了一个对象,这个对象就是 JuniorCoder.porotype,相当于 new Object() 得出的,所以 junior 的 __proto__ 下的 __proto__ 就是 Object.prototype,这就是原型链的顶端,在里面我们还可以看到 toString 方法等等

  3. 访问原型链上属性

    JuniorCoder.prototype.basicSkill = "html/css";
    JuniorCoder.prototype.sex = "man";
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    this.age = 22;
    }
    var junior = new JuniorCoder();
    SeniorCoder.prototype = junior;
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    var senior = new SeniorCoder();
    console.log(senior.age);
    console.log(senior.sex);
    // 22
    // man

    senior 可以访问 junior 本身的属性,也可以访问 JuniorCoder.prototype 上的属性,因为 junior 被挂载到了 SeniorCoder.prototype 上

    JuniorCoder.prototype.basicSkill = "html/css";
    function JuniorCoder() {
    this.lowerSkill = "javascript";
    this.years = 3;
    }
    var junior = new JuniorCoder();
    SeniorCoder.prototype = junior;
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    var senior = new SeniorCoder();
    senior.years++;
    // 等同于 senior.years = senior.years + 1;
    console.log(senior.years);
    console.log(junior.years);
    // 4
    // 3

    可以看到,通过 senior 试图改变 years 属性并不能真正影响 junior 的 years 属性,实际上只是在 senior 下创建了新的 years 属性,并将 junior.years 加一的结果赋值给它

Object.creat()

  1. Object 的 creat 方法用于创建对象,参数指定 prototype,可以为对象或 null

    var test = {
    name: "obj"
    }
    var obj = Object.create(test);
    console.log(obj.name);
    console.log(obj.__proto__ == test);
    // obj
    // true
  2. Object.creat(null)

    var obj = Object.create(null);
    console.log(obj);
    document.write(obj);
    // {}
    // 报错

    控制台显示 obj 是一个空对象,没有任何属性,包括 __proto__,如果使用 document.write(obj) 则会报错,因为 document.write 方法会把参数转成字符串再打印在页面,默认调用 toString() 方法,toString 方法需要从原型链上继承而来,而 obj 是一个完全的空对象,没有原型链,也没有 toString 方法,所以会报错

基于原型的继承

  1. 利用原型链实现继承

    JuniorCoder.prototype.basicSkill = "html/css";
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    this.age = 22;
    }
    var junior = new JuniorCoder();
    SeniorCoder.prototype = junior;
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    var senior = new SeniorCoder();

    senior 继承了 junior 的自身属性及原型链

  2. call/apply 实现继承

    function JuniorCoder(lowerSkill) {
    this.lowerSkill = lowerSkill;
    }
    function SeniorCoder(lowerSkill, advancedSkill) {
    JuniorCoder.apply(this, [lowerSkill]);
    this.advancedSkill = advancedSkill;
    }
    var senior = new SeniorCoder("javascript", "vue");

    继承了 JuniorCoder 实例的自身属性,不能继承原型链

  3. 公共原型继承

    JuniorCoder.prototype.basicSkill = "html/css";
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    }
    SeniorCoder.prototype = JuniorCoder.prototype;
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    var senior = new SeniorCoder();

    senior 继承 JuniorCoder 实例的原型链,不继承自身属性,但是改动 SeniorCoder.prototype 会影响 JuniorCoder.prototype

  4. 中间对象继承(圣杯模式)

    JuniorCoder.prototype.basicSkill = "html/css";
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    }
    Buffer.prototype = JuniorCoder.prototype;
    function Buffer() {}
    SeniorCoder.prototype = new Buffer();
    function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    SeniorCoder.prototype.basicSkill = "markdown";
    console.log(SeniorCoder.prototype.basicSkill);
    console.log(JuniorCoder.prototype.basicSkill);
    // markdown
    // html/css

    继承原型链,不继承自身属性,prototype 不相互影响,这种继承方式更为实用

    进行封装以后,更适应企业级开发

    JuniorCoder.prototype.basicSkill = "html/css";
    
    function JuniorCoder() {
    this.lowerSkill = "javascript"
    } function SeniorCoder() {
    this.advancedSkill = "vue";
    }
    inherit(SeniorCoder, JuniorCoder);
    SeniorCoder.prototype.basicSkill = "markdown";
    console.log(new SeniorCoder());
    console.log(new JuniorCoder()); function inherit(Target, Origin) {
    Target.prototype = Object.create(Origin.prototype);
    Target.prototype.constructor = Target;
    Target.prototype.superClass = Origin;
    }

    使用 Object 的 creat 方法直接创建中间对象,将 construtor、superClass 属性设置好,便于分析和维护

hasOwnProperty()

判断属性是否是实例对象本身的,如果是则返回 true

Car.prototype.brand = "BMW";
function Car() {
this.color = "red";
}
var car = new Car();
console.log(car.hasOwnProperty("brand"));
console.log(car.hasOwnProperty("color"));
// false
// true

instanceOf

判断实例对象的原型链上是否有某个构造方法

JuniorCoder.prototype.basicSkill = "html/css";

function JuniorCoder() {
this.lowerSkill = "javascript"
} function SeniorCoder() {
this.advancedSkill = "vue";
}
inherit(SeniorCoder, JuniorCoder); function inherit(Target, Origin) {
Target.prototype = Object.create(Origin.prototype);
Target.prototype.constructor = Target;
Target.prototype.superClass = Origin;
} var senior = new SeniorCoder(); console.log(senior instanceof SeniorCoder);
console.log(senior instanceof JuniorCoder);
console.log(senior instanceof Object);
// true
// true
// true

JavaScript 原型与继承的更多相关文章

  1. 深入理解:JavaScript原型与继承

    深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解. 首先JavaScr ...

  2. JavaScript原型与继承

    JavaScript原型与继承 原型 在JavaScript中,每个函数都有一个prototype属性,这个属性是一个指针,指向该函数的原型对象.这个原型对象为所有该实例所共享.在默认情况下,原型对象 ...

  3. JavaScript原型与继承的秘密

    在GitHub上看到的关于JavaScript原型与继承的讲解,感觉很有用,为方便以后阅读,copy到自己的随笔中. 原文地址:https://github.com/dreamapplehappy/b ...

  4. javascript原型链继承

    一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constru ...

  5. JavaScript 原型与继承机制详解

    引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困 ...

  6. 8条规则图解JavaScript原型链继承原理

    原形链是JS难点之一,而且很多书都喜欢用一大堆的文字解释给你听什么什么是原型链,就算有图配上讲解,有的图也是点到为止,很难让人不产生疑惑. 我们先来看一段程序,友情提示sublimeText看更爽: ...

  7. 【Javascript】Javascript原型与继承

    一切都是对象! 以下的四种(undefined, number, string, boolean)属于简单的值类型,不是对象.剩下的几种情况——函数.数组.对象.null.new Number(10) ...

  8. 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链

    1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...

  9. JavaScript原型及继承

    一.浅谈原型 首先我们要知道创建对象的方法有两种: 1.通过字面量的方式直接创建 var obj = { name:'baimao', age:21 } 2.通过构造函数创建对象 function P ...

随机推荐

  1. 《内蒙古自治区第十二届大学生程序设计竞赛试题_D: 正品的概率》

    问题 D: 正品的概率 内存限制:128 MB时间限制:1 S标准输入输出 题目类型:传统评测方式:文本比较上传者:外部导入 提交:36通过:7 返回比赛提交提交记录 题目描述 袋中有m枚正品硬币,n ...

  2. 吴裕雄--天生自然 人工智能机器学习实战代码:LASSO回归

    import numpy as np import matplotlib.pyplot as plt from sklearn import datasets, linear_model from s ...

  3. Waymo

    技术优势 Waymo在自己的激光雷达系统上投入了大量资金,它认为这项技术对自动驾驶汽车的长期成功至关重要.实际上,该公司声称它已经将专有激光雷达传感器的成本降低了90%,这种传感器以前的制造成本为7. ...

  4. Rime输入法一些设定

    有鉴于谷歌搜狗拼音等不太好用,但是博主一直页没找到合心的输入法,直到遇见Rime,中州韵就是我想要的输入法.记录一下自己用的时候的修改,以备查询.注意:缩进不要弄丢,所有更改完都需要重新部署才能生效. ...

  5. 中国的规模优势,有望帮助AI芯片后来者居上?

    ​芯片一直是个神奇的东西,表面上看是电脑.笔记本.智能手机改变了世界,其实,真正改变世界的硬件内核是芯片,芯片相关的技术才是科技界最实用.最浪漫的基础技术,也正因如此,谁掌握了芯片基础技术,谁就能立于 ...

  6. 《和莎莫的 500 天》中为什么 Summer 最终没有和 Tom 在一起?

    好的电影总是需要仔细赏味几次,每次也都会有不同的收获.就像我钟爱的[500 days of summer]. 彪悍的大胡子导演MarcWebb实在是太有趣,把自己的亲身经历搬上大荧幕,因为" ...

  7. Hadoop2.7搭建

    Hadoop最底部是 Hadoop Distributed File System(HDFS),它存储Hadoop集群中所有存储节点上的文件.HDFS(对于本文)的上一层是MapReduce 引擎,该 ...

  8. Day 1 模拟

    1. P1088 火星人 利用STL中的next_permutation();函数求一种排列的下一种排列,循环m次即为答案.(STL大法好~~C++是世界上最好的语言~~逃 #include < ...

  9. iOS Swift 开发语言之初接触,纯代码创建UIView,UITableView,UICollectionView

    1. 初始化Label设置AttributeString override func viewDidLoad() { let label = UILabel(frame:CGRect(x:,y:,wi ...

  10. 前端笔记--css样式笔记

    一.浮动 定位布局 1.浮动布局 left 元素向左浮动 right 元素向右浮动 例如:设置2个按钮,要使得按钮在同一行位置摆放,可以使用浮动,令按钮浮动到右边.注意,先设置float的按钮,例如: ...