前言

  阅读本文前先来思考一个问题,我们在 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. Datahero Inc利用区块链溯源,造福各行各业

    近些年来,随着区块链技术的不断崛起以及快速发展,越多越多的人提出将区块链技术引入到溯源系统当中,溯源也成为了区块链技术的重要应用场景之一. 目前,Datahero inc已建设一整套的溯源平台系统,基 ...

  2. [Python学习笔记]组织文件

    shutil 模块 shutil 模块可以让我们很方便的在Python程序中复制.移动.改名和删除文件. 复制文件和文件夹 使用shutil.copy()来复制文件,该函数含两个参数,均为字符串格式的 ...

  3. spring-ioc心得

    1.创建spring容器,严格的来说就是创建ClassPathXmlApplicationContext对象, 该对象属于ApplicationContext类型(是一个接口)该接口下有很多实现类, ...

  4. idea更改包名无法加载主类解决

    把工程下面的.idea目录下的workspace.xml里面的路径改成你最新的路径即可. <option name="SPRING_BOOT_MAIN_CLASS" valu ...

  5. 一文了解Python的迭代器的实现

    本文对迭代器的解释参考自:https://www.programiz.com/python-programming/iterator 最后自己使用迭代器实现一个公平洗牌类. 博主认为,理论来自实践,假 ...

  6. BurpSuite生成快捷方式

    Win下 在win系统写个脚本,能方便快捷的打开burp. burp.bat @echo oFF if "%1" neq "1" ( >"%te ...

  7. 剑指 Offer 04. 二维数组中的查找 (思维)

    剑指 Offer 04. 二维数组中的查找 题目链接 本题的解法是从矩阵的右上角开始寻找目标值. 根据矩阵的元素分布特性, 当目标值大于当前位置的值时将row行号++,因为此时目标值一定位于当前行的下 ...

  8. 原生js日历选择器,学习js面向对象开发日历插件

    在web开发过程中经常会碰到需要选择日期的功能,一般的操作都是在文本框点击,然后弹出日历选择框,直接选择日期就可以在文本框显示选择的日期.开发好之后给用户使用是很方便,但如果每一个日历选择器都要临时开 ...

  9. CNN结构演变总结(二)轻量化模型

    CNN结构演变总结(一)经典模型 导言: 上一篇介绍了经典模型中的结构演变,介绍了设计原理,作用,效果等.在本文,将对轻量化模型进行总结分析. 轻量化模型主要围绕减少计算量,减少参数,降低实际运行时间 ...

  10. rest framework ViewSet

    ViewSets 路由选择确定要用于一个请求哪个控制器之后,控制器负责做出请求的感并产生相应的输出. - Ruby on Rails的文档 Django的REST框架允许你的逻辑一组在一个类中的相关意 ...