前言
在之前JavaScript中暂时的错误的介绍new的底层原理,本篇文章将带来new的正确的底层原理
万物皆对象
原型对象(显示原型)
- 函数定义出来天生拥有一个属性protype(原型),是一个对象
- 意义 : 1.让函数构造中的一些固定的属性和方法挂在到原型上,在创建实例的时候,就不需要重复的执行这些属性和方法
- 挂载在原型上的属性和方法,在实例对象上可以直接访问到
- 实例对象是无法修改原型上的属和方法的
- 实例对象也无法删除原型上的属性和方法
Person.prototype.age = 11;
function Person() {
this.name = "pp";
// this.age = 18;
}
const p1 = new Person();
console.log(p1.age + " " + p1.name);//实例对象p1可以直接访问挂载在原型上的属性和方法 p1.name
结果图:
Person.prototype.say = function () {
console.log("我是pp");
};
Person.prototype.age = 11;
function Person() {
this.name = "pp";
}
const p1 = new Person();
p1.say();
p1.say = "hello";//添加了给构造函数添加了一个显式属性,并没有修改我们原型对象上的属性
console.log(p1.say);
console.log(p1.say());
结果图:
注意:这里需要注意的是,这里看似是成功修改了p1的say()方法,将方法修改为属性,但是这并不是修改而是添加了一个新的属性say在构造函数中,因为我们是新创建的一个对象,这个步骤相当于添加一个属性在p1上,覆盖了我们原型默认的,那我们如果就是想要原来默认的say()方法,可以使用Person.prototype.say()
结果图:
对象原型(隐式原型)
1.v8引擎中,每一个对象都拥有一个属性__proto__,这个属性值也是一个对象
2.v8在访问对象中的属性时,会先访问对象上的显示拥有的属性(构造函数有的属性,或者我们在对象上添加的属性),如果找不到,就回去对象上的隐式原型(对象原型)中查找,也就是__proto__中查找
3.实例对象的隐式原型===构造函数的显示原型
4.实例对象的隐式原型===构造函数的显示原型 这是为什么呢? new原理导致的
5.这么设计意义是什么?
让实例对象继承到构造函数原型上的方法和属性,方便我们为某一个数据类型添加属性和方法 如下: 为什么引用数据类型的数组身上有push方法?
const arr=[]
arr.push(1)
这个过程相当于
const arr=new Array()//默认指行,arr其实是对象,万物皆对象
arr.__proto__===Array.prototype
[JavaScript : 万物皆对象?带你理解对象创建过程和数据类型的判断 引言:万物皆对象 在JavaScript中 - 掘金](url)
所以arr的push()是来自Array()的原型对象上的
new 的原理
1.创建一个空对象
2.让构造函数的this执行这个空对象
3.执行构造函数中的代码
3.将空对象的隐式原型,__proto__赋值成构造函数的显示原型
5.返回这个对象
var obj={}//1
Person.call(obj)//2让this===obj这的call()方法会在下一篇文章讲到
this.name='pp'3
obj.__proto__=Person.prototype//4
return obj//5
constructor
实例对象的对象原型(隐式原型)和构造函数的原型(原型对象/显示原型)上都有constructor属性,但对象原型__proto__的constructor是继承自原型对象prototype的,对象原型__proto__是被原型对象prototype赋值的,所以它们的constructor是相同的,constructor都是指向创建对象原型和原型对象的构造函数的。
- 意义:是为了让所有实例对象都知道自己是从哪个构造函数创建的
Person+()是调用函数,在函数
Person 内部,如果没有显式返回一个对象,那么构造函数会默认返回新创建的对象(即实例)。但是,如果我们把 Person 当作普通函数调用(而不是通过 new),如果 Person 函数没有返回值(即没有 return 语句),则返回 undefined,如果 Person 函数有返回值,则返回该值。
第二个Person这是引用,从上到下查找变量或函数名,这只有Person函数,Person代表的就是Person函数体 那么构造函数与对象原型、原型对象之间的关系如下图:
原型链
那么函数身上的原型对象是对象,那原型对象也有对象原型(隐式原型),函数也是对象,函数也是对象,函数也有对象原型(隐式原型)
v8在访问对象的属性时,会先访问对象上的显示拥有的属性,如果找不到,就回去对象上的隐式原型中查找,也就是__proto__中查找,如果还找不到,就会去对象原型__proto__的对象原型__proto__中查找,层层往上,直到找到或者找不到为止null
v8的这种链状关系就叫原型链
function grandParent(){
//grandParent.prototype=new Object();
// grandParent.prototype.__prototype=Object.prototype;
// Object.prototype.__prototype__ == null
this.name = "grandParent";
this.card = "idcard";
}
Parent.prototype = new grandParent();//{name:"grandParent",card:"idcard"}
function Parent() {
this.LastName = "张";
}
Child.prototype = new Parent();//{LastName:"张"}.__proto__ = {name:"grandParent",card:"idcard"}
function Child() {
this.name = "张三";
this.age = 18;
}
const child = new Child();
console.log(child.LastName);
console.log(child.card);
</script>
不是每个对象都有原型,没有原型的对象
- 创建对象的一种方式 const obj=Object.create()
- Object.create(obj)创建一个新对象,让这个新对象的隐式原型等于传入的obj
- Objecr.create(null)得到一个没有原型的对象
1.Foo构造函数
f2,f2实例化对象. __proto__ ===Foo.prototype
f1,f2的对象原型被Foo构造函数的原型(原型对象/显示原型)赋值
Foo.prototype.constructor指向Foo构造函数
Foo构造函数也是对象,Foo.__proto__===Function.prototype
所有函数都由Fuction创建,所有函数.__proto___===Function.prototype
2.Function构造函数
Fuction构造函数实例对象的对象原型===Fuction.prototype
Fuction.prototype.__proto__===Object.prototype
3.Object构造函数
Object构造函数实例对象o1,o2的对象原型===Object.prototype
Object.prototype的对象原型__proto__最终指向空
Object.prototype.constructor指向Object构造函数
Object构造函数是函数,所以其对象原型===Fuction.prototype
所有原型对象.__proto__===Object.prototype