深入浅出JS原型链
2020/3/18 5:01:16
本文主要是介绍深入浅出JS原型链,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
原型链作为js中的一座大山,算是面试过程中的必问之一;理解好原型链,对于学习js也有很大的帮助。
定义
我们先来看看它的定义
当js在一个对象上查找属性的时候,首先查找对象本身的属性(即声明时候定义的属性),如果在这些属性中没有找到目标属性,那么js会找它的__proto__
对象,如果这个对象没有,那么继续找__proto__
对象的__proto__
对象,直到__proto__
为null
或者找到目标属性为止...... 这个__proto__的关系链,就是我们说的原型链
特性
其实原型链真的很简单,你只要记住下面这三个点就完了。
- 每一个对象都有
__proto__
属性 - 每一个函数都有
prototype
属性,这个属性值也是一个对象 - 每一个对象的
__protp__
都会指向它的构造函数的prototype
实践
俗话说实践出真知,我们一步步来验证这几个特性.
验证第一点
大家可以分段复制我的代码粘贴到浏览器控制台中
let obj = {name:"leelei"}; console.log(obj.__proto__); let arr = [1,2,3] console.log(arr.__proto__); let str = "http://www.leelei.info"; console.log(str.__proto__); let num = 100; console.log(num.__proto__);
当你全部打印完以后,你会发现一个问题,诶,我的str和num不是基础类型吗?不是对象也有proto
属性?
这里我们要提一下js的隐式转化规则了,当你访问基础类型的一些属性或者调用一些方法的时候,你的基础类型会被js给包装起来,就像下面这样
str.__proto__ => new String(str).__proto__; num.__proto__ => new Number(num).__proto__;
那么这个new String
和new Number
哪里来的噶?
它们都是js原生的构造函数,总共有以下几种
- Object
- Number
- String
- Boolean
- RegExp
- Error
- Function
- Array
- Date
我们现在已经验证了第一点了,每个对象(基础类型是通过原生构造函数转成了对象)都有__proto__属性
验证第二点
请一行一行复制以下代码到控制台中
console.log(Object.prototype); console.log(Number.prototype); console.log(String.prototype); console.log(Boolean.prototype); console.log(RegExp.prototype); console.log(Error.prototype); console.log(Function.prototype); console.log(Array.prototype); console.log(Date.prototype);
如果我们不是构造函数呢
function fn(){} console.log(fn.prototype);
可以看到,无论是构造函数,还是我们声明的函数都有prototype
的属性。
区别只是原生的构造函数他们的prototype
对象上已经自带了一些方法和属性。
那么综上我们可以验证第二个特性,每一个函数都有prototype
属性
验证第三点
我们只要判断下第一步的__proto__是否指向第二步对应构造函数的prototype即可。
let obj = {name:"leelei"}; obj.__proto__ === Object.prototype; //true let arr = [1,2,3] arr.__proto__ === Array.prototype; //true let str = "http://www.leelei.info"; str.__proto__ === String.prototype; //true let num = 100; num.__proto__ === Number.prototype; //true
结果已经显而易见了,第三个论点也可得到论证
从定义出发
我们根据定义来感受一下这个链条
let obj = {name:"leelei"}; console.log(obj.__proto.valueOf); //有这个方法嗷 obj.valueOf(); //{name:"leelei"};
我们可以看到,在我们声明obj的时候并没有这个属性方法,但是我们却可以调用,我们打印了obj的__proto__对象,发现这里存在这个方法,所以可以调用。
这里就是定义所讲的当对象本身没有这个属性的时候,我们会去它的__proto__对象上找
这里才只有一层噶?如果隔多几层会不会调用不了了阿?
我们可以编写一个构造函数,然后给它的原型对象prototype添加自定义方法来试验。
function Foo(age){this.age = age}; Foo.prototype.say = function(){console.log('hello')}; let foo = new Foo(18);
根据特性2
,对象的__proto__对象指向构造函数的prototype,所以这样是没什么问题的
foo.say() => foo.__proto__.say() //hello 相当于=> Foo.prototype.say() //hello
那我们还能不能使用Object上面的方法呢?当然可以
foo.valueOf() => foo.__proto__.valueOf 相当于=> Foo.prototype.valueOf 怎么没有嗷? //第一层找不到喔,那就往上一层 //不要忘了prototype也是对象! //所以它也有__proto__属性 ==> Foo.prototype.__proto__.valueOf 相当于==> Object.prototype.valueOf 找到啦
你可能会有点好奇为什么这两个会等于?Foo.prototype.__proto__ === Object.prototype
因为Foo.prototype
是对象,Foo
是函数
Foo.__proto__ === Function.prototype; Foo.prototype.__proto__ === Object.prototype;
一点需要注意的
typeof (Function.prototype); //function
这个居然不是对象?!惊了
根据特性2
,每个函数都有一个prototype属性。
既然Function.prototype
不是对象而是方法,那么它也有prototype属性才对
Function.prototype.prototype //undefined
惊了!它居然没有prototype属性!
莫慌,除这个之外,都是适用的。记住这个特例即可。
总结
其实原型链并不复杂,但是单纯简单记忆的话确实比较难记,我们可以自己在控制台多捣鼓一下,按照它的定义来走,更方便我们理解和记忆。
谢谢大家,如果有错误,请在评论区指出。
打个广告,李雷的博客
这篇关于深入浅出JS原型链的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-05HTML 颜色
- 2024-10-05HTML 颜色名
- 2024-10-01AntDesign-Form-rules学习:轻松入门教程
- 2024-10-01classnames学习:轻松掌握前端中的类名管理
- 2024-09-30前端案例资料:新手入门必读教程
- 2024-09-30前端编程资料:新手入门必备教程
- 2024-09-30前端培训资料:新手入门必读教程
- 2024-09-30滚动吸顶项目实战:从入门到上手
- 2024-09-29HTML学习:span标签教程详解
- 2024-09-29HTML基础:button标签教程