JS中原型、原型链的理解

1.构造函数

构造函数模式的目的就是为了创建一个自定义类,并且创建这个类的实例。构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的,即实例识别。构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。

另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。

复制代码
function Person(name, age, gender) {    //创建一个构造函数 
        this.name = name
        this.age = age
        this.gender = gender
        this.sayName = function () {    //定义构造函数中的一个方法
            alert(this.name);
        }
    }
    var per = new Person("孙悟空", 18, "");     //调用创建的构造函数,必须用new关键字调用
​
    console.log(per)            //当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
//toSting(): 可以把一个Number转换为字符串
复制代码

每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)

2.原型对象

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中在JavaScript中。每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。

在JavaScript中是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说不应该能够获取到这个值的,但是现在浏览器中实现了 __proto__属性来访问这个属性,但是最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,可以通过这个方法来获取对象的原型。

3.原型链

3.1 .__proto__和constructor

每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。

function Person() {}
    var person = new Person()
    console.log(person.__proto__ === Person.prototype)//true
    console.log(Person.prototype.constructor===Person)//true
    //顺便学习一个ES5的方法,可以获得对象的原型
    console.log(Object.getPrototypeOf(person) === Person.prototype) // true

3.2 何为原型链?

在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止(person → Person → Object),这样就形成了一个原型指向的链条,专业术语称之为原型链

 

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。注意:Object对象是老祖宗,没人比他更大了,所以Object的_proto_为空, 即原型链的尽头一般来说都是 Object.prototype

console.log(Object.prototype.__proto__ === null) // true

我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

复制代码
function Person() {}                                               //创建构造函数
    Person.prototype.a = 123;                                       //给构造函数的原型实例中添加a属性,而不是在构造函数中添加a属性
    Person.prototype.sayHello = function () {                     //给构造函数的原型实例中添加sayHello方法,而不是在构造函数中添加该方法
      alert("hello");
    };
​
    var person = new Person()                                      //创建一个构造函数实例对象
    console.log(person.a)//123                                  // 虽然在构造函数中没有a属性,但是在其父函数(实例原型)中有该属性,所以构造                                                                函数继承了a属性,仍能正常输出而不报错
    console.log(person.hasOwnProperty('a'));//false                // hasOwnProperty方法仅检查对象自身是否含有某属性,a属性是继承的,自身中没                                                                有,所以不能正常输出     
    console.log('a'in person)//true                             // 若当前实例对象的构造函数中没有属性a时,in方法可以查询其父函数或祖父函数中                                                                是否有a属性,即查户口查到祖宗十八代去了,23333         
​
​
//getPrototypeOf():返回参数内部特性Prototype的值
console.log(Object.getPrototypeOf(person1) == Person.prototype)  //true
复制代码

4.原型链指向

p.__proto__  // Person.prototype
Person.prototype.__proto__  // Object.prototype
p.__proto__.__proto__ //Object.prototype
p.__proto__.constructor.prototype.__proto__ // Object.prototype
Person.prototype.constructor.prototype.__proto__ // Object.prototype
p1.__proto__.constructor // Person
Person.prototype.constructor  // Person

5.如何获取对象中非原型链上的属性?

使用后hasOwnProperty()方法来判断属性是否属于原型链的属性

function iterate(obj){
   var res=[];
   for(var key in obj){
        if(obj.hasOwnProperty(key))
           res.push(key+': '+obj[key]);
   }
   return res;
}

function关键词本身也有一个原型对象”Function”,其他函数在使用“function”关键词时,一定存在属性继承于原型对象“Function.prototype”

原型的作用

假如没有原型,那我们需要这样声明一个对象

复制代码
var obj = {
toString: window.Object.prototype.toString,
hasOwnPropertyOf: window.Object.......
}
obj.toString()
var obj2 = {
toString: window.Object.prototype.toString,
hasOwnPropertyOf: window.Object.......
}
obj2.toString()
复制代码

 

有了原型,我们只需要一句话就可以搞定了

let obj = {}

所以原型的作用就是让你无需重复声明共有属性,省代码,省内存。 ​

来源 | https://blog.csdn.net/qq_38867012/article/details/124001609

posted @   shi-tao  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示