zoukankan      html  css  js  c++  java
  • JS 原型和原型链

    原型 

    JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。

    function Person(name, age){
        this.name = name
        this.age = age
        this.sayName = function(){
            alert(this.name)
        }
    }
    
    var person1 = new Person('张三', 18)
    var person2 = new Person('李四', 26)

    在上面的代码中,Person函数是一个构造函数,函数内部定义了 name,age属性,sayName方法,所有实例对象(person1person2)都会有这些属性和方法。通过构造函数为实例对象定义属性和方法,有一个缺点就是每个方法都要在每个实例上重新创建一遍,浪费内存。

    于是我们就重构一下,把 sayName 函数放在 Person 函数的外面,然后在 Person 构造函数中的 this.sayName 指向外部的 sayName 全局函数,变成了下面的样子

    var sayName = function(){
        alert(this.name)
    }
    function Person(name, age){
        this.name = name
        this.age = age
        this.sayName = sayName
    }
    var person1 = new Person('张三', 18)
    var person2 = new Person('李四', 26)

     但是如果 Person 里面有100个方法,那么就需要定义100个全局函数,这样做有个缺点,定义的全局函数只能给 Person 用,函数容易被污染,而且没有封装性可言。

    能不能把 Person 的属性和方法全部放在一个对象中,让实例对象person1,person2等共享它,能够直接使用?答案是可以的。

    JavaScript 规定,每个函数都有一个prototype属性,指向一个对象(原型对象),这个对象就能够让实例对象共享属性和方法。所以代码可以改成下面这样

    function Person(name, age){    
        this.name = name
        this.age = 18
    }
    Person.prototype.country = '中国'
    Person.prototype.sayName = function(){
        console.log(this.name)
    }
    var person1 = new Person('张三', 18)
    var person2 = new Person('李四', 26)
    person1.sayName() // 张三
    person2.sayName() // 李四
    person1.country  // 中国
    person2.country  // 中国
    

     JS 又给对象提供了 __proto__ 属性去访问共享属性和方法的对象,那么 person1.__proto__ 获取的对象就是 Person.prototype,它们在内存中都指向 addr=700 的地址,所以 person1.__proto__ === Person.prototype 。

    __proto__prototype 的区别

    __proto__ 是实例对象内部的一个属性,它指向对象的原型,在原型链上查找方法及属性时使用的便是 __proto__

    prototype 是函数上的属性。

    原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。

    Person.prototype.country = '美国'
    
    person1.country // 美国
    person2.country // 美国

    如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。

    person1.country // 美国
    person1.country = '俄罗斯'
    
    person1.country // 俄罗斯
    person2.country // 美国
    Person.prototype.country //美国

    总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

    __proto__ 取值

    // 1.对于使用对象字面量创建的对象,这个值是 Object.prototype。
    var obj = {}
    obj.__proto__ === Object.prototype
    
    // 2.对于使用数组字面量创建的对象,这个值是 Array.prototype。
    var arr = []
    arr.__proto__ === Array.prototype
    
    // 3.对于函数来说,这个值是 Function.prototype
    var fn1 = function(){}
    fn1.__proto__ === Function.prototype // true
    
    // 4.对于使用 new func() 方式创建的对象,这个值就是 func.prototype
    var num1 = new Number(1)
    num1.__proto__ === Number.prototype // true
    
    var str1 = new String('hello')
    str1.__proto__ === String.prototype // true

    所以我们就得到了一个公式:

    var 对象 = new 函数()
    对象.__proto__ === 对象的构造函数.prototype

    进入烧脑环节

    person1.__proto__ === Person.prototype //true
    typeof Person.prototype // "object"
    
    // 规则一:在 JavaScript 中所有其他对象都继承自 Object 对象
    
    // 那么我们把 Person.prototype 看作是一个整体,套用上面的公式
    Person.prototype.__proto__ === Object.prototype // true
    
    // 规则二:在 JavaScript 中, 每个函数实际上都是一个Function对象。
    Person.__proto__ === Function.prototype // true
    
    // 我们再把 Function.prototype,套用公式,运用规则一,就得到下面的结果
    Function.prototype.__proto__ === Object.prototype // true
    
    // Function 的特殊情况
    Function.__proto__ === Function.prototype // true
    Function.__proto__ === Object.prototype // false
    // 总要有一个终点的嘛
    Object.prototype.__proto__ === null  // true

    原型链

    JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

    如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOftoString方法的原因,因为这是从Object.prototype继承的。

    那么,Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是nullnull没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null

    Object.prototype.__proto__ === null // true
    

    读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined,这样的查过过程就是原型链查找。

  • 相关阅读:
    DataTable 只保留想要的几列
    如何用多个字符串来切分字符串
    用.net 发送邮件
    sqlserver 行列转换
    sql面试题一 学生成绩
    将DataReader转换为DataTable
    C# 如何用多个字符串来切分字符串并去除空格
    SqlServer按时间自动生成生成单据编号
    实验一 Java开发环境的熟悉
    Java学习笔记心得——初识Java
  • 原文地址:https://www.cnblogs.com/wubh/p/js_prototype_and_proto.html
Copyright © 2011-2022 走看看