前言

  阅读本文前先来思考一个问题,我们在 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. NGK.IO会是一个投资优质项目吗?

    互联网发展至今,技术已经高度成熟,人们发现了互联网的好处后,互联网逐渐渗入到家家户户.随着时代的变迁,人们对HTTP长期作为主流霸占互联网食物链的顶端感到不满足.当人类开始变得挑剔,HTTP的劣势就逐 ...

  2. .Net按模板导出Excel

    最近在项目中遇到需求 需要按照一定的模板导出数据 还是直接上代码 这里贴一部分模板长什么样吧 然后就是代码 大致就是找到模板 复制一份临时文件 然后修改临时文件然后导出数据 代码如下 string a ...

  3. 【从零开始撸一个App】Fragment和导航中的使用

    Fragment简介 Fragment自从Android 3.0引入开始,它所承担的角色就是显而易见的.它之于Activity就如html片段之于页面,好处无需赘述. Fragment的生命周期和Ac ...

  4. MongoDB语句命令

    更新列名 db.xx.update({}, {$rename : {"StoreId" : "MetaId"}}, false, true) 查询长度 db.g ...

  5. 代码生成器 springboot架构

    import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.IdType; ...

  6. IDEA总结

    1. 什么是idea? idea是Java开发软件 2. IDEA下载 https://www.jetbrains.com/idea/download/download-thanks.html?pla ...

  7. Go的map

    目录 map 一.map的创建 1.map的类型 2.定义并初始化 二.给map添加元素 三.获取map的元素 四.删除map的元素 五.获取map的长度 六.map的类型 七.map的相等性 八.循 ...

  8. 从零开始使用 webpack5 搭建 react 项目

    本文的示例项目源码可以点击 这里 获取 一.前言 webpack5 也已经发布一段时间了,其模块联邦.bundle 缓存等新特性值得在项目中进行使用.经过笔者在公司实际项目中的升级结果来看,其提升效果 ...

  9. 后端程序员之路 31、Protocol Buffer

    google/protobuf: Protocol Buffers - Google's data interchange formathttps://github.com/google/protob ...

  10. java list集合遍历时删除元素

    转: java list集合遍历时删除元素 大家可能都遇到过,在vector或arraylist的迭代遍历过程中同时进行修改,会抛出异常java.util.ConcurrentModification ...