js--原型和原型链相关问题
前言
阅读本文前先来思考一个问题,我们在 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--原型和原型链相关问题的更多相关文章
- 对js原型及构造函数的相关理解
一.js中的原型创建(声明)一个函数,浏览器在内存中会创建一个对象.每个函数都默认会有一个属性prototype指向了这个对象,就是说prototype的属性的值就是这个对象.此对象就是该函数的原型对 ...
- js之原型,原型链
1.原型是什么? 在构造函数创建出来的时候,系统会默认的创建并关联一个对象,这个对象就是原型,原型对象默认是空对象 默认的原型对象中会有一个属性constructor指向该构造函数 原型 ...
- Js笔记(对象,构造函数,原型,原型链,继承)及一些不熟悉的语法
对象的特性: 1.唯一标识性,即使完全不一样的对象,内存地址也不同,所以他们不相等 2.对象具有状态,同一个对象可能处在不同状态下 3.对象具有行为,即对象的状态可能因为他的行为产生变迁 Js直到es ...
- js小记:对象、原型及原型链、面向对象编程
一.js对象 1.js对象 js对象是一种复合数据类型,它可以把多个(不同类型的)数据集中在一个变量中,并且给每个数据起名字. 2.对象与数组 对象的每个数据有对应的名字(属性名),我们通过叫名字访问 ...
- 深入理解JS对象和原型链
函数在整个js中是最复杂也是最重要的知识 一个函数中存在多面性: 1.它本身就是一个普通的函数,执行的时候形成的私有作用域(闭包),形参赋值,预解释,代码执行,执行完 成后栈内存销毁/不销毁. 2.& ...
- JS原型、原型链、构造函数、实例与继承
https://cloud.tencent.com/developer/article/1408283 https://cloud.tencent.com/developer/article/1195 ...
- 攻略前端面试官(三):JS的原型和原型链
本文在个人主页同步更新~ 背就完事了 介绍:一些知识点相关的面试题和答案 使用姿势:看答案前先尝试回答,看完后把答案收起来检验成果~ 面试官:什么是构造函数 答:构造函数的本质是一个普通函数,他的特点 ...
- 原型链污染(Node.js污染,javasrcipt原型链污染的)
学习链接: https://www.jianshu.com/p/6e623e9debe3 关于NJS https://xz.aliyun.com/t/7184 相关题是 GYCTF ez_expr ...
- Js 原型和原型链
Js中通过原型和原型链实现了继承 Js对象属性的访问,首先会查找自身是否拥有这个属性 如果查到,则返回属性值,如果找不到,就会遍历原型链,一层一层的查找,如果找到就会返回属性值 直到遍历完Object ...
随机推荐
- JUC并发集合类CopyOnWriteList
CopyOnWriteList简介 ArrayList是线程不安全的,于是JDK新增加了一个线程并发安全的List--CopyOnWriteList,中心思想就是copy-on-write,简单来说是 ...
- JS广度优先遍历
自己用JS实现了 广度优先遍历 第一种用了数组的高阶函数,看起来有些复杂.然后思索着从可读性上优化了一下,孰优孰劣以后分析. var list = [{ id: "ab", chi ...
- mysql 8.0.18 小白安装教程
1. 下载 官网下载:https://dev.mysql.com/downloads/mysql/ 嫌官网网速慢可以加q群,在群文件里下载: 1.下载第一个download 2.解压在自己建的目录(各 ...
- 1102 Invert a Binary Tree——PAT甲级真题
1102 Invert a Binary Tree The following is from Max Howell @twitter: Google: 90% of our engineers us ...
- SpringCloud Sleuth
1.定义 Sleuth(分布式请求链路跟踪):提供了一套完整的服务跟踪解决方案,也兼容zipkin. 参考网址:https://github.com/spring-cloud/spring-cloud ...
- JUC-ThreadLocalRandom
目录 Radndom类的局限性 ThreadLocalRandom 这个类是在JDK7中新增的随机数生成器,它弥补了Random类在多线程下的缺陷. Radndom类的局限性 在JDK7之前包括现在j ...
- POJ-1751(kruskal算法)
Highways POJ-1751 注意这里的样例答案也是对的,只是输出顺序改变,但是这也没关系,因为题目加了特殊判断. #include<iostream> #include<cs ...
- POJ-2195(最小费用最大流+MCMF算法)
Going Home POJ-2195 这题使用的是最小费用流的模板. 建模的时候我的方法出现错误,导致出现WA,根据网上的建图方法没错. 这里的建图方法是每次到相邻点的最大容量为INF,而花费为1, ...
- HDOJ-6685(暴力+思维)
Rikka With Coin HDOJ-6685 主要的思想如下: 首先10元的硬币最多只会用一个,如果用了两个,直接替换成一个10元.一个20元一定不亏. 20元的硬币最多只会用三个,如果用了四个 ...
- C++ 中的虚函数表及虚函数执行原理
为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...