前言

  阅读本文前先来思考一个问题,我们在 js 中创建一个变量,我们并没有给这个变量添加一些方法,比如 toString() 方法,为什么我们可以直接使用这个方法呢?如以下代码,带着这样的问题,我们来学习本节的原型和原型链的一些知识。

正文

  1.构造函数创建对象问题

     function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}
var person = new Person("xiaoming", 18);
person.sayName(); // xiaoming

  当使用构造函数创建对象person时,即使用new操作符构造一个实例对象的时候,首先会创建一块新的内存空间,标记为person实例,执行构造函数会创建一个对象,会将对象的原型指向构造函数的 prototype 属性,然后执行上下文中的this指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象,创建的对象有一个构造函数 constructor 属性。其实质就是创建一个 object 引用类型的实例,然后把实例保存在变量 person 中。

  总结 :new 操作符调用构造函数经历了以下四步:

  (1)创建一个新对象;

  (2)将构造函数的作用域赋值给新对象(因此 this 指向这个新对象);

  (3)执行构造函数中的代码(为这个构造的新对象添加属性);

  (4)返回这个新对象。

  2.原型相关问题

  我们创建的每个函数都有一个 prototype 属性,这个属性是一个指针,指向一个对象,而这个对象包含了通过该构造函数实例的对象所共享的属性和方法。那么,prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。

      function Person() {}
Person.prototype.name = "xioming";
Person.prototype.sayHello = function () {
console.log(this.name);
};
var person1 = new Person();
person1.sayHello(); //xiaoming
var person2 = new Person();
person2.sayHello(); //xiaoming

  上面的代码,我们将 sayHello() 方法和所有属性直接添加到了 Person 的 prototype 属性中,构造函数变成了空函数,通过此构造函数创建的新对象实例,具有相同的属性和方法,与纯构造函数创建的对象不同的是所有实例共享了这些属性和方法。

  (1)理解原型对象

  无论什么时候,只要创建一个新函数,就会根据一种特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象,默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。例如上面的例子中 Person.prototype.constructor 指向 Person,同样,我们可以继续为原型对象添加别的属性和方法。创建了自定义构造函数之后,其原型只会取得constructor属性,其他方法都是通过从object继承而来,当调用构造函数创建一个新实例后,该实例内部会包含一个指针指向构造函数的原型对象,这个指针叫 __proto__ ,因此可以通过下面的图来表示上面例子的代码。

  因此通过上面的图不难得出,js 中获取原型的方法有如下三种:

  (1)person1.__proto__

  (2)Object.getPrototype(person1)

  (3)person1.constructor.prototype

  同样可以通过 isPrototypeOf() 方法来确定对象之间是否存在原型关系,Person.prototype.isPrototypeOf( person1 )返回值为 true 。当然也可以如下使用Object.getPrototype(person1).name返回值为“ xiaoming ”。再来看下下面的这段代码:

     function Person() {}
Person.prototype.name = "xioming";
Person.prototype.sayHello = function () {
console.log(this.name);
};
var person1 = new Person();
person1.name="xiaohong"
console.log(person1.name);//“xiaohong”

  通过上面的代码不难得出对象属性的访问顺序,每当代码中读取某个对象的属性是,都会执行一次搜索,目标是给定名字的属性,搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值,如果没有找到,则会继续搜索__proto__指针指向的原型对象,在原型对象中找到具有相同名字的属性。虽然可以通过对象实例访问保存在原型对象中的值,但是不能通过独享实例重新原型中的值,如果我们在实例中添加一个属性,而该属性与原型中的属性同名,那么就在实例中创建该属性,该属性就会屏蔽原型对象中的那个属性。即添加了同名属性后,这个属性就会阻止我们访问原型中的属性。即使我们把这个属性值设置为null,也只会在实例中访问这个属性,不会恢复对原型的同名属性的访问,要想恢复,只能使用 delete 操作符完全删除该实例属性。因此,官方也提供了一个 hasOwnProperty() 方法来判断该属性是否属于实例对象,如果是则返回 true,否则返回 false 。

  最后总结得出原型,构造函数,实例对象三者之间的关系如下:

  (2)原型与 in 操作符

  使用 in 操作符有两种情况,一种是直接使用,另外一种是 for- in循环中使用,在单独使用的时候,in 操作符会在通过对象能够访问属性的时候返回 true ,无论该属性存在于对象实例还是原型对象中。"name" in person1 返回 true ,因此结合 hasOwnProperty() 方法可以判断属性是存在于自身实例中,还是存在于原型对象中。使用 for-in 循环时,返回的是所有能够在对象中可以访问,可以枚举的属性,其中既包含实例中的有包含原型中的,tostring(),valueOf()....但是由于浏览器版本限制,这种方式不推荐使用。

  要取得对象上所有可枚举的实例属性,可以通过 Object.keys() 方法,该方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

  (3)原型的原型关系如下图:

  2.原型链

  理解原型链之前,先来看如下代码:

     function Person() {}
Person.prototype.name = "xioming";
Person.prototype.sayHello = function () {
console.log(this.name);
};
var person1 = new Person();
console.log(person1.toString()); //[object Object]

  这就是刚开始讲到的,为什么每一个对象都包含 tostring() 这个方法呢,有了原型的了解,这里用到原型链,当我们访问一个对象的属性或者方法时,如果这个对象实例内部不存在这个属性或者方法,那么他会在原型对象里找这个同名属性或者方法,这个原型对象又有自己的原型,于是这么一层一层找下去,也就产生了原型链这个概念,原型链的尽头一般来说都是 Object.prototype ,所有者就产生了新建的对象都会存在 toString() 等方法。

总结

  以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。

js--原型和原型链相关问题的更多相关文章

  1. 对js原型及构造函数的相关理解

    一.js中的原型创建(声明)一个函数,浏览器在内存中会创建一个对象.每个函数都默认会有一个属性prototype指向了这个对象,就是说prototype的属性的值就是这个对象.此对象就是该函数的原型对 ...

  2. js之原型,原型链

    1.原型是什么?    在构造函数创建出来的时候,系统会默认的创建并关联一个对象,这个对象就是原型,原型对象默认是空对象    默认的原型对象中会有一个属性constructor指向该构造函数  原型 ...

  3. Js笔记(对象,构造函数,原型,原型链,继承)及一些不熟悉的语法

    对象的特性: 1.唯一标识性,即使完全不一样的对象,内存地址也不同,所以他们不相等 2.对象具有状态,同一个对象可能处在不同状态下 3.对象具有行为,即对象的状态可能因为他的行为产生变迁 Js直到es ...

  4. js小记:对象、原型及原型链、面向对象编程

    一.js对象 1.js对象 js对象是一种复合数据类型,它可以把多个(不同类型的)数据集中在一个变量中,并且给每个数据起名字. 2.对象与数组 对象的每个数据有对应的名字(属性名),我们通过叫名字访问 ...

  5. 深入理解JS对象和原型链

    函数在整个js中是最复杂也是最重要的知识 一个函数中存在多面性: 1.它本身就是一个普通的函数,执行的时候形成的私有作用域(闭包),形参赋值,预解释,代码执行,执行完 成后栈内存销毁/不销毁. 2.& ...

  6. JS原型、原型链、构造函数、实例与继承

    https://cloud.tencent.com/developer/article/1408283 https://cloud.tencent.com/developer/article/1195 ...

  7. 攻略前端面试官(三):JS的原型和原型链

    本文在个人主页同步更新~ 背就完事了 介绍:一些知识点相关的面试题和答案 使用姿势:看答案前先尝试回答,看完后把答案收起来检验成果~ 面试官:什么是构造函数 答:构造函数的本质是一个普通函数,他的特点 ...

  8. 原型链污染(Node.js污染,javasrcipt原型链污染的)

    学习链接: https://www.jianshu.com/p/6e623e9debe3 关于NJS  https://xz.aliyun.com/t/7184 相关题是 GYCTF  ez_expr ...

  9. Js 原型和原型链

    Js中通过原型和原型链实现了继承 Js对象属性的访问,首先会查找自身是否拥有这个属性 如果查到,则返回属性值,如果找不到,就会遍历原型链,一层一层的查找,如果找到就会返回属性值 直到遍历完Object ...

随机推荐

  1. GoEasy使用阿里云OSS出现的问题

    前言:本人使用goeasy来实现微信小程序里面和其他人的im临时对话窗口,想要实现可以同时发送语音和视频.图片.表情包的话,就要通过goeasy关联到阿里云的存储对象. 报错:The OSS Acce ...

  2. 离场定高转弯DF与CF的对比

    也许是刚学会CAD的缘故,配合风螺旋插件,画图的感觉真是蛮爽的,忍不住画了一张又一张. 接着昨天的离场保护区,我们来聊一下PBN指定高度转弯保护区的画法.指定高度转弯的计算本身没有太多复杂的地方,真正 ...

  3. Python学习笔记_爬虫数据存储为xlsx格式的方法

    import requests from bs4 import BeautifulSoup import openpyxl wb=openpyxl.Workbook() sheet=wb.active ...

  4. 03_MySQL重置root密码

    重设root密码

  5. react性能提升

    1.把.bind(this)提升到constructor里面 2.在生命周期函数里面shouldComponentupdate里面做父组件改变重新渲染以致于子组件重新渲染的禁止 3.在setstate ...

  6. docker封装vue项目并使用jenkins发布

    一.概述 vue项目可以打一个dist静态资源包,直接使用Nginx发布即可. 现在由于要上docker,需要将vue项目和nginx打成一个镜像才行. 项目结构如下: ./ ├── build │  ...

  7. 后端程序员之路 39、一个Protocol Buffer实例

    实际工作的Protocol Buffer使用经验 # 写proto文件- 协议版本 项目用的是protobuf2,所以要指定 syntax = "proto2";- 包名 pack ...

  8. 后端程序员之路 20、python复习

    Welcome to Python.orghttps://www.python.org/ 怎么用最短时间高效而踏实地学习 Python? - 知乎https://www.zhihu.com/quest ...

  9. pytorch(07)数据模型的读取

    DataLoader与Dataset pytorch中的数据读取机制 graph TB DataLoader --> DataLoaderIter DataLoaderIter --> S ...

  10. redhat安装python3.7

    下载并解压: 1 wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz 2 tar -xzvf Python-3.7.2.tgz ...