Javascript 进阶|原型与原型链

Javascript 进阶|原型与原型链

从问题开始,

  • 1、 例子 1 , 我们要创建两个人对象,可以直接用对象定义赋值; 但是这样带来的问题是, 假如我有 1 万,2 万个人, 那不是要定义很多对象
  • 2、 为了解决例子 1 的问题,例子 2 中,我们可以创建一个专门建立人的对象的函数(这个专门建立对象的函数就是构造函数,其实也是函数的一种),而 javascript 中可以用 new 关键字创建对象 ,如例子 3(new 约等于隐藏了 函数开头生成 this 的对象 同时最后返回 this)
  • 3、 从例子 2、3 中, 即使我们从创建了 两个对象, 它们的方法事实上还是指向了不同的地方。同样的,问题来了,如果是两个对象还好,但是我们如果创建 1 万,2 万个, 那内存空间就会占用很多, 事实上,在例子中所有的 walk 方法都是一样的,我们有没有办法只用一个 walk 呢? 答案就是 原型
  • 4、 例子 3 中, 事实上, 对象本身没有 walk 方法, walk 方法 是在 该对象的原型上, 那它怎么可以调用得到 walk 方法呢? 那就是通过 原型链
1、 例子 1 , 我们要创建两个人对象,可以直接用对象定义赋值; 但是这样带来的问题是, 假如我有 1 万,2 万个人, 那不是要定义很多对象

    console.log(" 对象 - 例子 1:");

    var xiaoa = { name: "xiaoa", walk: function () { console.log(this.name + " walk") } };
    var xiaob = { name: "xiaob", walk: function () { console.log(this.name + "walk") } };

    xiaoa.walk();
    xiaob.walk();

    console.log("xiaoa.walk == xiaob.walk?: ", xiaoa.walk == xiaob.walk);
    // false
    console.log("== 例子 1 end ==");

2、 为了解决例子 1 的问题,例子 2 中,我们可以创建一个专门建立人的对象的函数(这个专门建立对象的函数就是构造函数,其实也是函数的一种),而 javascript 中可以用 new 关键字创建对象 ,如例子 3(new 约等于隐藏了 函数开头生成 this 的对象 同时最后返回 this)

    console.log(" 对象 - 例子 2:");
    var createObject = function (name) {
        var obj = {};
        obj.name = name;
        obj.walk = function () { console.log(this.name + " walk") };
        return obj;
    }

    var xiaoc = createObject('xiaoc')
    var xiaod = createObject('xiaod')

    xiaoc.walk();
    xiaod.walk();

    console.log("xiaoc.walk == xiaod.walk?: ", xiaoc.walk == xiaod.walk);
    // false

    console.log("== 例子 2 end ==");

    console.log(" 对象 - 例子 3:");
    var createObject = function (name) {
        this.name = name;
        this.walk = function () { console.log(this.name + " walk") };
    }

    // new 约等于隐藏了 函数开头生成 this 的对象 同时最后返回 this
    var xiaoe = new createObject('xiaoe')
    var xiaof = new createObject('xiaof')

    xiaoe.walk();
    xiaof.walk();

    console.log("xiaoe.walk == xiaof.walk?: ", xiaoe.walk == xiaof.walk);
    // false
    console.log("== 例子 3 end ==");

3、 从例子 2、3 中, 即使我们从创建了 两个对象, 它们的方法事实上还是指向了不同的地方。同样的,问题来了,如果是两个对象还好,但是我们如果创建 1 万,2 万个, 那内存空间就会占用很多, 事实上,在例子中所有的 walk 方法都是一样的,我们有没有办法只用一个 walk 呢? 答案就是 原型

实际上,声明一个构造函数 ,除了会申请保存函数的内存空间,还会额外申请一个内存空间,用于存储构造函数 A 的原型对象; 在原型对象中默认有一个 constructor 属性存储了构造函数的地址 (引用)(也就是 constructor 指向了构造函数), 而 createObject.prototype 下面的函数就可以被调用并且只占一份内存! 看例子 4:

    console.log(" 对象 - 例子 4:");
    
    var createObject = function (name) {
        this.name = name;
    }
    
    // 声明一个构造函数 ,除了会申请保存函数的内存空间,还会额外申请一个内存空间,用于存储构造函数 A 的原型对象; 在原型对象中默认有一个 constructor 属性存储了构造函数的地址 (引用)(也就是 constructor 指向了构造函数)
    console.log("createObject.prototype.constructor == createObject ?: " , createObject.prototype.constructor == createObject ) 
    console.log("createObject prototype: " , createObject.prototype ) 

    createObject.prototype.walk = function () { console.log(this.name + " walk") };

    var xiaog = new createObject('xiaog')
    var xiaoh = new createObject('xiaoh')

    xiaog.walk();
    xiaoh.walk();

    console.log("xiaog.walk == xiaoh.walk?: ", xiaog.walk == xiaoh.walk);
    // true
    // 所以原型的作用: 共享属性,节省内存空间。

    console.log("== 例子 4 end ==");

原型的作用: 共享属性,节省内存空间。

4、 例子 3 中, 事实上, 对象本身没有 walk 方法, walk 方法 是在 该对象的原型上, 那它怎么可以调用得到 walk 方法呢? 那就是通过 原型链

当调用对象里的方法时候, 如果该对象有此方法的时候, 它会调用自己的 walk; 而没有, 它才会调用 原型的 walk 方法。 那系统怎么知道要调用 原型的 walk 方法 呢?
同函数中的原型 (自动加了 prototype 对象) 类似, 而对象在创建时候,也会自动加了一个 __proto__ 属性,用于指向它的原型; 同理的, 函数的 prototype 是一个对象, 所以它也有 __proto__ 属性;
所以可以看得到 新建对象 的 __proto__ 是 构造函数的 prototype 对象 ; 构造函数的 prototype 对象 的 __proto__ 最后是 Object.prototype ; Object.prototype.__proto__ 的值为 null 。这样会形成由 __proto__ 将对象和原型连起来的链条。这就是原型链。

看例子 5


    console.log(" 对象 - 例子 5:");
    var createObject = function (name) {
        this.name = name;
    }

    createObject.prototype.walk = function () { console.log(this.name + " walk") };

    var xiaog = new createObject('xiaog')
    var xiaoh = new createObject('xiaoh')


    xiaog.walk();//xiaog special walk
    xiaoh.walk();//xiaoh walk

    xiaog.walk = function () {
        console.log(this.name + " special walk")
    }

    xiaog.walk();//xiaog walk
    xiaoh.walk();//xiaoh walk
    console.log("xiaog.walk == xiaoh.walk?: ", xiaog.walk == xiaoh.walk);
    // false

    // 从例子看得到,当该对象如果有 walk 方法 的时候, 它会自己调用自己的 walk; 而没有 walk 方法 的时候 , 它才会调用 原型的 walk 方法

    // 那系统怎么知道要调用 原型的 walk 方法 呢? 
    // 同函数中的原型 (自动加了 prototype 对象) 类似, 而对象在创建时候,也会自动加了一个 __proto__ 属性,用于指向它的原型

    console.log("createObject.prototype == xiaog.__proto__: ", createObject.prototype == xiaog.__proto__);
    // true

    // 所以我们用 call 方法调用, 可以返回原型的 walk 方法
    xiaog.__proto__.walk.call(xiaog); //xiaog walk

    console.log("xiaog.prototype.walk == xiaoh.walk?: ", xiaog.__proto__.walk == xiaoh.walk);
    // true

    // 既然 对象在创建时候,也会自动加了一个 __proto__ 属性,用于指向它的原型 , 同理的, 函数的 prototype 是一个对象, 所以它也有 __proto__ 属性

    // 在这里 createObject.prototype 的 __proto__ 是 Object.prototype
    
    console.log("createObject.prototype.__proto__ == Object.prototype?: ", createObject.prototype.__proto__ == Object.prototype);
    // true


    // 所以可以看得到 xiaog 的  __proto__ 是 createObject.prototype ; createObject.prototype 的 __proto__ 是 Object.prototype ; Object.prototype.__proto__ 的值为 null 。这样会形成由 __proto__ 将对象和原型连起来的链条。这就是原型链


    console.log("== 例子 5 end ==");

一个小扩展是: 当我们用 变量 赋值 var arr = new Array(), 时, 其实我们用到了的函数,譬如 join, concat,find …… 事实上都是 Array.prototype 对象的函数,而如果调用 isPrototypeOf, 就是 Array.prototype.__proto__ 的 Object.prototype 的 函数。 同理其他 内置对象 如 String Function, Nubmer Boolean …… 也一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值