写在前面:
很多人分不清__proto__和 prototype ,下面先举个例子:
prototype 是你自己打工挣得的一笔钱,而__proto__是你爹打工挣的钱,当你被你爹 new 出来的时候,他的钱也就是你的钱,但是如果你有兄弟姐妹,你爹的钱他们也可以使用,但是prototype是属于你自己的钱,你的兄弟姐妹用不了。简单来说就是,你爹(构造函数) 每创造出一个 孩子(实例),你爹自己的钱 (prototype:你爹也是人,他的钱属于他自己的prototype)就等于你(实例)的__proto__
1.原型初步认识
let arr = ["hello"];
console.log(arr.concat("world"));
如下图,__proto__属性就是 Array 对象的原型,因为arr 是Array的实例化对象,所以arr也可以使用 Array原型上的方法
let a = {};
let b = {};
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b));//true
2.没有原型的对象
let hq = { name: "hq" };
console.log(hq.hasOwnProperty("name"));//true
//完全数据字典对象
let obj = Object.create(null, {
name: {
value: "hello",
},
});
console.log(obj);
let hd = {
render() {
console.log("hd render");
},
};
hd.__proto__.render = function () {
console.log("hq");
};
hd.render();//hd render
*自己有就用自己的,没有才会到原型上找
4.函数拥有多个长辈
function User() {}
console.dir(User)
function User() {}
User.prototype.show = function () {
console.log("hello");
};
let hq = new User();
console.log(User.prototype === hd.__proto__);//true
*proto:服务于函数对象
function User() {}
User.__proto__.view = function () {
console.log("user view");
};
User.view();//user view
*函数可作为构造函数,也可作为函数对象
5.原型关系与属性继承实例
function User() {}
console.dir(User.__proto__=== Function.prototype);//true
如上代码,因为 User 其实是通过 new Function 创建的,所以User 的原型__proto__就=== Function 的 prototype
User.prototype.__proto__ === User.__proto__.__proto__; //true
如上代码,我们先来分析 User.prototype 就是User这个构造函数自身的原型,然而这个原型prototype它本身也是一个对象,而这个prototype对象的其实是通过Object 构造出来的。我们再看User.proto,因为User 是通过new Function 来的,所以 它的__proto__就等于Function.prototype。而Function的prototype和User的prototype一样都是通过Object创造出来的对象,所以它们的原型__proto__就相等
6.系统构造函数的原型
let arr = []; //new Array
console.log(arr.__proto__ === Array.prototype);//true
let str = ""; //new String
console.log(str.__proto__ === String.prototype);//true
let bool = true; //new String
console.log(bool.__proto__ === Boolean.prototype);//true
let reg = /a/i; //new RegExp
console.log(reg.__proto__ === RegExp.prototype);//true
let obj = {}; //new Object
console.log(obj.__proto__ === Object.prototype);//true
多看一眼
console.dir(Object.__proto__)
输出如下:
妥妥的一个函数
没错,Object也是通过Function构造出来的。
7.自定义对象的原型设置
*设置原型:setPrototypeOf
let obj = {};
let parent = {name: "parent"};
Object.setPrototypeOf(obj, parent);
let obj = {};
let parent = { name: "parent", age: 18 };
Object.setPrototypeOf(obj, parent);
console.log(Object.getPrototypeOf(obj));
*通过原型的constructor 可以找到构造函数
function User() {}
console.log(User.prototype.constructor === User);//true
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
show() {console.log(this.name)},
};
let ls = new User("李四");
ls.show();//李四
9.基于实例创造对象
先看看如下代码:
function Admin() { }
let obj = new Admin();
console.log(obj.__proto__.constructor === Admin);//true
大白话:实例化对象的原型__proto__当中的 constructor 属性指向的是构造函数本身
再理解:
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
};
}
let ls = new User("李四");
function createByObj(obj, ...args) {
//找到obj原型的constructor
const constructor = Object.getPrototypeOf(obj).constructor;
return new constructor(...args);
}
let zs = createByObj(ls, "张三");
zs.show();
10.原型检测之instanceof
function A() { }
let a = new A();
console.log(a instanceof A)//true
function A() { }
function B() { }
let b = new B();
A.prototype = b
let a = new A();
console.log(a instanceof B)//true
11.Object.isPrototypeOf原型检测
let a = {}
let b = {}
// b 对象是否在 a 对象的原型链上
console.log(b.isPrototypeOf(a))//false
console.log(Object.prototype.isPrototypeOf(a))//true
//把b对象设置为a对象的原型
Object.setPrototypeOf(a, b)
console.log(b.isPrototypeOf(a))//true
12.in 与 hasOwnProperty的属性检测
in:检测一个属性是否存在于一个对象或者该对象的原型链上
let a = { url: 'baidu.com' }
let b = { name: 'baidu' }
console.log('url' in a)//true
Object.prototype.c = 'hello'
console.log('c' in a)//true
Object.setPrototypeOf(a, b)
console.log('name' in a)
hasOwnProperty:检测当前对象,不检查其原型链上是否存在某一属性
let a = { url: 'baidu.com' }
let b = { name: 'baidu' }
Object.setPrototypeOf(a, b)
console.log(a.hasOwnProperty('name'))//false
let a = { url: 'baidu.com' }
let b = { name: 'baidu' }
Object.setPrototypeOf(a, b)
for (const key in a) {// 会遍历到原型链上的属性
console.log(key)
}
for (const key in a) {
if(a.hasOwnProperty(key))
console.log(key)
}
原型检测:
instanceof: 一个对象 的原型链上是否有 某个构造函数 的 prototype
isPrototypeOf:一个对象 是否在 另一个对象 的原型链上
属性检测:
hasOwnProperty:检测 当前对象,不检查其原型链上是否存在 某一属性
in:检测 一个属性 是否存在于 一个对象 或者 该对象的原型链 上
13.使用call或apply借用原型链
let obj = { data: [1, 3, 2, 6, 5] }
Object.setPrototypeOf(obj, {
max() {
return this.data.sort((a, b) => b - a)[0]
}
})
let zs = {
lessons: { js: 34, php: 53, node: 66 },
get data() {
return Object.values(this.lessons)
}
}
console.log(obj.max.apply(zs))//66
let obj = { data: [1, 3, 2, 6, 5] }
Object.setPrototypeOf(obj, {
max(data) {
return data.sort((a, b) => b - a)[0]
}
})
let zs = {
lessons: { js: 34, php: 53, node: 66 },
}
console.log(obj.max.call(null, Object.values(zs.lessons)))//66
14.DOM节点借用Array原型方法
<body>
<button message='1' class="one">1</button>
<button message='2'>1</button>
<script>
let btns = document.querySelectorAll('button')
btn = Array.prototype.filter.call(btns, item => {
return item.hasAttribute('class')
})
console.log(btn[0].innerHTML);//1
</script>
</body>
15.合理的构造函数方法声明
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name)
}
}
let ls = new User('李四')
let zs = new User('张三')
console.log(ls);
console.log(zs);
面向对象中,只保存对象属性, 对于show方法没必要开辟独立内存造成浪费
如下,将show方法放到原型中
function User(name) {
this.name = name;
}
User.prototype.show = function () {
console.log(this.name);
}
let ls = new User('李四')
let zs = new User('张三')
let obj = {
name: '张三',
show() {
console.log(this.name)
}
}
Object.setPrototypeOf(obj, {})
obj.show()//张三
无论原型怎么改,我们的this永远指向调用它的对象
let obj = {
name: '张三',
}
let User = {
name: '李四',
show() {
console.log(this.name)
}
}
Object.setPrototypeOf(obj, User)
obj.show()//'张三'
17.__proto__是属性访问器
*严格意义来讲__proto__它不是一个属性,它是getter setter
let obj = { name: "张三" };
obj.__proto__ = {
show() {
console.log(this.name);
},
};
console.log(obj.__proto__);
如上,我们将obj的__proto__设置为别的对象,ok,没问题,但是请看下面
let obj = { name: "张三" };
obj.__proto__ = {
show() {
console.log(this.name);
},
};
obj.__proto__ = 99//重点
console.log(obj.__proto__);
(不是和上面同一张截图!)
没错,它输出的结果和上面一样,这是因为它是一个getter 和 setter ,对它的赋值操作进行了判断。
大概意思如下:
let obj = {
action: {},
get proto() {
return this.action;
},
set proto(value) {
if (value instanceof Object) {
this.actioin = value;
}
},
};
obj.proto = "abc";
console.log(obj.proto);//{}
如上,并没有将"abc"设置成功,但是如下设置为一个对象,那就能设置成功
obj.proto = {
show: function () {},
};
console.log(obj.proto);
霸王硬上弓
let obj = Object.create(null);
obj.__proto__ = 99;
console.dir(obj.__proto__);//99
通过Object.create让其不继承Object 的原型,使其成为孤儿,这样就可以随意设置它的__proto__属性(此时就是属性了)