理解原型链
原型是JavaScript区别与其他面向对象程序设计语言的最本质的区别,下面我们先通过一个小例子来理解原型链。
<script>
var arr = ["嘉然"];
console.log(arr.concat("向晚"));
console.log(arr);
</script>
这串代码相信很多同学都能看懂,就是我们所学的数组的基本方法,我们采用了数组的concat方法,表示在数组后插入一个新元素,并且不改变数组本身,返回添加后新的数组。
那么我们仔细思考一下,为什么一个arr对象里内置这莫多的方法,每创建一个对象时需要加载大量的数据,会拖慢,编译器的运行方式。
这时,有些学过Java的小伙伴就要说了,既然有好多方法都是共同的,那么我们可不可以有一个父类呀。
没错!JavaScript也有父类,不过他的父类叫做原型
点击他的 Prototype属性,发现这个里面有一系列方法,眼尖的小伙伴已经发现,这一系列方法中的 concat, push, shift, sort, splices等都是我们数组对象Array的常用方法,因此我们可以得出:
对于一个实例化的对象,他的Prototype是他的父类。
那么他的父类得Prototype是什么呢?
父类得Prototype是Object,而再往后就没有protoType了。
创建对象的方法
/*直接创建*/
var uncleRed = {
name: "uncle"
}
/*构造函数创建*/
function Person(name) {
this.name= name;
}
var xiaoXue =new Person("xiaoxue");
/*Object.create创建*/
var zimin = Object.create(null,{
name: "zimin"
});
前两种我们在JavaScript初级阶段已经有所了解,现在我们主要讲解以下第三种方法。
Object.create()方法有两个参数,第一个参数表示是这个对象的原型,第二个参数表示的是这个对象的属性。
以前两种形式创建的对象都具有默认的Object原型,通过Object.create()方法创建的对象可以没有原型。
需要掌握
方法调用优先级
var a = {
ShowName() {
console.log("a.name");
}
}
a.__proto__.ShowName = function() {
console.log("proto.name");
}
a.__proto__.dex = function() {
console.log("123");
}
console.log(a);
console.log(a.__proto__);
这里的在调用方法时,先搜索自己本身的,如果自己没有,就向上找他的原型的,原型也没有就报错。
prototype与proto
<script>
function Person(name) {
this.name = name;
}
var per1 = new Person("zimin");
console.log(Person.prototype);
console.log(per1.__proto__);
</script>
可以看到两者是一样的
这里我们可以先得出一个小结论
prototype 是针对函数而言的的,他是函数(构造函数)的一个属性,这个属性是一个对象。对象里放着一些公有的方法,我们可以通过书写构造函数的prototype对象里的方法实现对对象方法的扩充
而"__ proto __" 是针对一个实例化出的对象而言的,他是对象的一个属性,这个属性是是一个地址(指 针)指向的是生成这个对象的构造函数的prototype属性。通过这个 __ proto __可以找到prototype以实现对象使用构造函数的方法!
那么为什么上图的构造方法既有prototype属性,也有__ proto__属性呢?
function Person(name) {
this.name = name;
}
var per1 = new Person("zimin");
console.log(Person.prototype);
console.log(Person.__proto__);
因为,马克思说过:矛盾是对立统一的集合体,失去一侧另一侧也会失去。要求我们变换视角看问题。
从被new出来的对象per1角度来看,在他的眼里,Person是创造出他的构造函数,所以具有prototype属性。
而从所有函数的Function()构造函数来看,Person()构造函数只是被他创建出来的对象。他既然是一个对象那么他就有__ proto__属性,指向Function()的prototype属性。
<script>
function Person(name) {
this.name = name;
}
var per1 = new Person("zimin");
console.log(Person instanceof Function);//true
console.log(per1 instanceof Person);//true
</script>
instanceof 关键字是用于验证前者是不是由B创建出来的。 验证得Person()确实是构造函数Function创建出来的!
在实际开发中,prototype可以理解为这个构造函数的一个公用方法池,这个构造函数创建出的所有对象,都可以通过他的__ proto__方法来访问这个公用方法池!
补充
很多小伙伴问如何用Function构造函数构创建 function对象?这里就来展示一下
var Person = new Function("name","age", "this.name=name, this.age=age");
var per1 = new Person("小明", 12);
console.dir(Person);
console.dir(per1);
我们需要掌握
prototype方法池的constructor属性
constructor属性里存的是一个地址,指向的是这个构造函数本身。
开发用法!
我们有一个对象之后,用这个对象的__ proto__找到他的父亲,再通过prototype里constructor属性,找回构造函数就可以实现 ”给我一个对象,创造出更多对象"
function Person(name) {
this.name = name;
}
var p1 = new Person("zhang");
function createByConstructor(Obj, str) {
var constructor = Obj.__proto__.constructor;
return new constructor(str);
}
var p2 = createByConstructor(p1, "wang");
console.log(p2);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARYwu4K4-1655796889440)(C:\Users\31932\AppData\Local\Temp\1652613619105.png)]
Function 与Object的特殊性
我们前面讲过,按照一体两面性,一个构造函数既可以作为一个创建其他对象的构造函数,也可以作为被一个对象被上级的构造函数构造出来。那么作为对象的最上级Object和Function,他们有什么特殊性性质呢。
var obj = new Object();
obj.name= "zimin";
console.log(obj);
在这里我们看到Object也是有作为一种构造方法来存在的。既然他是构造方法,那么他就是由Function实力化出来的,因此Object作为对象而言,他的__ proto__应该是Function的prototype。
var obj = new Object();
obj.name= "zimin";
console.log(obj);
console.log(obj.__proto__ === Object.prototype); //true
console.log(Object.__proto__ === Function.prototype); //true
和我们预测的一致!
那么对于最高层Function呢?
我们知道,Function可以被new Function创建,但是Function本身也是一种构造函数,在不断溯源中,我们的可推出。
var f1 = new Function(“age”,“this.age = age”);//function本身也可作为一种构造函数
Function的__ proto__ 和prototype是相等的
验证如下:
console.log(Function.__proto__ === Function.prototype);
console.log(Object.__proto__ === Function.prototype);
console.log(Object instanceof Function);
console.log(Function instanceof Object );
console.log(typeof Object);
console.log(typeof Function);
前两个比较好理解,那么三四行Function和Object为什么相互为原型呢?
下面我们来着重分析一下
Function与Object的纠葛
我们不得不再提一句马克思的矛盾观点:“矛盾是一体两面的”
站在 new Object()的角度来看,Object是一个构造函数,他是构造函数,他是函数,他的prototype就是Function。
但是站在Function.属性名的角度来看,他有各种属性(至少有prototype属性),什么东西有属性呢?对象才有属性,所以Function是一个对象,那么 Function instanceof Object 当然是正确的!。
本节要点:
Object里有关原型的方法
Object.create(原型,新对象)
create方法用于在创建对象的时候指明它的原型
let Vtuber ={name:"向晚"};
let liYi = Object.create(Vtuber,{
hobby:{
value:"sing"
}
});
console.dir(Vtuber);
console.dir(liYi);
console.log(liYi.name);
可以看到我们新创建了一个对象李奕,她的原型被我们指定为了Vtuber,因此我们就可以根据原型链的调用,通过李奕来访问到向晚;
Object.setPrototype(对象名,原型)
这个用于给一个对象来添加他原型。但是要注意,在JavaScript中一个对象只能有一个原型
let Vtuber ={name:"向晚"};
let liYi = Object.create(Vtuber,{
hobby:{
value:"sing"
}
});
let singer = {
name:"粒Q"
}
Object.setPrototypeOf(liYi,singer);
console.log(liYi.name) //粒Q
在这段代码中,我们先给李奕在创建时指明原型向晚,随后我们又把让他的原型改为singer,最后访问李奕的name属性,得到的是新的原型的结果。
Object.getPrototype(对象名)
用于获取这个对象的原型,比较简单
原型链中的this指向
var Vtuber = {
name: "Asoul",
show() {
console.log("我们是" + this.name);
}
}
var Ava = {
name:"向晚"
}
Object.setPrototypeOf(Ava,Vtuber);
Ava.show(); //我们是向晚
我们用setPrototypeOf方法绑定Ava的原型为Vtuber,再调用Ava的show()方法,因为Ava里没有show方法所有跟随原型链向上寻找,找到Vtuber里的show()方法,这里的this指向Ava里的name。 因此我们得出结论: 谁调用方法,this指向谁
constructor
constructor 是构造函数方法池 Protototype 里面的一个属性,他是一个指针,指向这个构造函数方法池所对应的构造函数。
function User() {};
console.dir(User.prototype.constructor);
果真如此
对象创造世界
由于 指向构造函数的constructor属性存放在prototype内,并且由构造函数创建出来的对象的__ proto __ 属性也指向构造函数的prototype,因此我们可以由一个对象生成千万的对象。
function User(name) {
this.name = name;
};
var p1 = new User("zimin");
function creatByObject(obj,name) {
const constructor = Object.getPrototypeOf(obj).constructor;
return new constructor(name);
}
var p2 = creatByObject(p1,"Diana");
console.dir(p2);
在这个代码中,我们利用constructor属性可以通过对象来创建对象,这在实际开发中可以实现对象具体代码对用户透明,实现项目的解耦合。 (我们利用p1创建p2,实现构造函数透明化) 我们需要掌握
原型检测
instanceof检测 原型
a instanceof A 的作用原理是如果a的原型链向上找出现A的prototype,那么结果就是true,反之则是false; 这个很好理解唯一要注意的就是 Object和Function对象之间的instanceOf 我们前面已经讲过 Object的原型链中有Function的prototype因为Object可以当构造函数。Function的原型链中有Object的prototype,因为Function可以具有属性这样对象的基本特征。
var arr = [];
console.log(arr instanceof Array);
console.log(arr instanceof Object);
console.log(arr instanceof Function)
console.log(Object instanceof Function);
console.log(Function instanceof Object);
验证结果成立
.isPrototypeOf方法
a.isPrototypeOf(b),翻译为:a是b的原型吗?如果a是b的原型链的一份子,那么这个结果就是true。 注意,isPrototype方法是父级在前,而 instanceof则是子级在前
function User() {
//构造函数
}
var a = {
name:"zimin"
//对象
}
var b = {
name:"123"
}
Object.setPrototypeOf(User,a);
Object.setPrototypeOf(b,a);
console.log(a.isPrototypeOf(User));
console.log(a.isPrototypeOf(b));
console.dir(User);
结果验证成功
in
in关键字用于检测一个属性是否在这个对象以及这个对象所在的原型链上
var Ava = {
hobby:"sing"
}
var Diana = {
team:"Asoul~"
}
console.log("hobby" in Ava);//true
console.log("team" in Ava);//false
我们事先声明好了两个对象,用in关键字测试 Ava中是否含有相应的属性结果如下。 可以看出对于hobby属性Ava中有返回true,team属性Ava没有返回false
var Ava = {
hobby:"sing"
}
var Diana = {
team:"Asoul~"
}
Object.setPrototypeOf(Ava,Diana);
console.log("hobby" in Ava);//true
console.log("team" in Ava);//true
当我们给Ava绑定上prototype原型为Diana后结果如下。 都是true说明in关键字在 检测属性时会在原型链上检测
hasOwnProperty 方法
这个方法用于判断某个属性是否属于这个对象。 注意这个方法是不攀登原型链的!!
var Ava = {
hobby:"sing"
}
var Diana = {
team:"Asoul~"
}
Object.setPrototypeOf(Ava,Diana);
console.log(Ava.hasOwnProperty("hobby"));//true
console.log(Ava.hasOwnProperty("team"));//false
结果如下 可见hasOwnProperty这个方法突出一个 Own即指看自己有没有,不向原型链上查找。 我们需要掌握
|