验证上图原型链是否正确
console.log(Object.__proto__===Function.prototype,
Function.__proto__===Function.prototype,
Array.__proto__===Function.prototype,
String.__proto__===Function.prototype,
Number.__proto__===Function.prototype,
Boolean.__proto__===Function.prototype,
Date.__proto__===Function.prototype,
RegExp.__proto__===Function.prototype,
Error.__proto__===Function.prototype)
// true true true true true true true true true
对象拥有 “proto” 属性,函数没有此属性,函数则拥有 prototype属性
对象可由函数生成
当我们生成一个对象时,对象中的”proto”属性则指向函数prototype属性—(默认值)
在上图中,有proto,什么可以设置了proto链接呢?
new、Object.create()和Object.setPrototypeOf(obj, prototype)可以设置proto
Object.setPrototypeOf(obj, prototype)语法知识
obj:可以是一个对象,也可以是一个函数
prototype:null或者一个对象
说明一下,proto这种写法是浏览器厂商自己的实现。
new底层实现原理
前提知识:构造函数有return和没有return两种情况。
构造函数里没有显式调用return时,默认是返回this对象,也就是新创建的实例对象。
当构造函数里调用return时,分两种情况:
1.return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。
这种情况下,忽视return值,依然返回this对象。
2.return的是Object,这种情况下,不再返回this对象,而是返回return语句的返回值。
下面是无return的实现原理:
1、创建一个新空对象
2、将构造函数的prototype赋值给新对象的proto
3、将构造函数的this指向该新对象
4、执行构造函数的代码语句
5、返回这个空对象
原理代码实现
_new = function() {
var obj = {};
obj.__proto__ = _new.prototype;
_new.call(obj);
//将构造函数的this执行该新对象
return obj;
}
验证上述实现原理:
function Fn() {
this.age = "chenxinhui";
}
Fn.prototype.study = function() {
console.log("验证new")
}
var fn = new Fn();
console.log(fn.__proto__ === Fn.prototype)
//true
console.dir(fn)
可以看出实例fn对象的proto指向了构造函数Fn的prototype属性。
再来一个例子验证构造函数的this的proto指向构造函数的prototype
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj); //执行new之后,this.__proto__===_.prototype
this._wrapped = obj;
};
var obj1=new _();
console.log(obj1);
验证上述问题,你可能对instanceof到底做了什么感到疑惑,那咱再来分析分析它的底层实现原理。
先说明以下结论
1、js规定,访问对象属性的时候,如果该对象下面没有这个属性,则去它下面的proto属性去寻找,如果还是没有就一直向下寻找,直到没有proto为止,即执行null
2、instanceof关键字返回boolean值,从左侧值引用的proto一层一层寻找,看右侧的prototype是否在左侧的原型链中
instanceof底层源码实现原理
function _instanceof(L, R) {
var rv = R.prototype;
L = L.__proto__;
while (true) {
if (L === null)
return false;
if (rv === L)
return true;
L = L.__proto__;
}
}
验证上述原理
function Father() {}
function Son() {}
Son.prototype = new Father();
//此处注意Son.prototype对象重新赋值,和Son.prototype.eat=function(){}区别
var son=new Son();
console.log(son instanceof Son)//true
console.log(son instanceof Father)//true
console.log(son instanceof Son)验证逻辑
function _instanceof(L, R) {//L为son,R为Son
var rv = R.prototype;//rv即为Son.prototype,指向了father
L = L.__proto__;//L指向了father
while (true) {
if (L === null)
return false;
if (rv === L)//通过,返回true
return true;//此时,两方都指Father的实例对象father,所以true
L = L.__proto__;
}
}
console.log(son instanceof Father)验证逻辑
function _instanceof(L, R) {//L为son,R为Father
var rv = R.prototype;//rv即为Father.prototype,指向了father
L = L.__proto__;//L指向了father
while (true) {
if (L === null)
return false;
if (rv === L)//第一次循环不通过
// 第二次循环son.__proto__.__proto__指向Father.prototype,通过,返回true
return true;
L = L.__proto__;//将L= L.__proto__.__proto__指向Father.prototype,继续循环
}
}
typeof
typeof返回值字符串,具体有"object","function","number","string","boolean","underfined",
es6中新增了"symbol",es10新增"bigint"
其中
typeof []==="object"//true
typeof null==="object"//true
typeof {}==="object"//true
apply和call
他们负责改变this的指向,他们两个的区别就是参数类型不同
apply(obj,array)//第二个参数记忆技巧,apply首字母和array首字母一样,为数组
call(obj,arg1,arg2,....)//
另外一些小知识点:
this要在执行时才能确认值,定义时无法确认。
函数执行上下文:
一段<script>中或者一个函数之内都会生成“执行上下文”
针对<script>,会生成全局的执行上下文,将变量声明、函数声明拿出来
针对一个函数,会生成函数执行上下文,在函数执行之前,将函数内的变量声明、函数声明、this、arguments拿出来
还要注意函数表达式不会生成执行上下文
函数声明: function fn() {…}
函数表达式:var a = function(){…},相当于执行了两部
var a;
a=funcition(){}