原型对象
个人笔记,方便自己看,另外感谢后盾人向军老师。本笔记是通过观看向军老师视频得出。
每个对象都有一个原型prototype对象,通过函数创建的对象也将拥有这个原型对象。原型是一个指向对象的指针。
- 可以将原型理解为对象的父亲,对象从原型对象继承来属性
- 原型就是对象除了是某个对象的父母外没有什么特别之处
- 所有函数的原型默认是
Object的实例,所以可以使用toString/toValues/isPrototypeOf等方法的原因 - 使用原型对象为多个对象共享属性或方法
- 如果对象本身不存在属性或方法将到原型上查找
- 使用原型可以解决,通过构建函数创建对象时复制多个函数造成的内存占用问题
- 原型包含
constructor属性,指向构造函数 - 对象包含
__proto__指向他的原型对象
Object.getPrototypeOf() //某个对象的原型
let arr = {};
let a = {};
console.log(Object.getPrototypeOf(arr)) //Object
console.log(Object.getPrototypeOf(a)) //Object
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(arr)) //true
let dog = Object.create(null,{name:{value:"dahuang"}})
创造一个原型链为null的对象
let cat = { name: 'xiaohua' };
let dog = Object.create(null, {
name: {
value: 'dahuang'
}
});
console.log(Object.getPrototypeOf(cat));
console.log(Object.getPrototypeOf(dog));//null
console.log(dog);
在原型上添加方法
let cat = {
sayName() {
console.log('xiaohua');
}
}
cat.__proto__.sayDad = function () {
console.log('proto')
}
cat.sayName();//xiaohua
cat.sayDad();//proto
cat.sayMOm();
当父级和自己有同一个方法时,很明显,自己的优先使用
使用 setPrototypeOf 与 getPrototypeOf 获取与设置原型
let child = { name: 'datou' };
let parent = {
name: 'xiaotou',
sayName() {
return this.name;
}
};
Object.setPrototypeOf(child, parent);
console.dir(child)
console.log(child.sayName());//datou
console.log(Object.getPrototypeOf(child))
使用一个实例对象来创造一个实例对象
function User(name, age = 2) {
this.name = name;
this.age = age;
}
// User.prototype.show = function () {
// console.log(this.name);
// }//因为 User.prototype重新赋值,所以已经没有show这个方法了
User.prototype = {
constructor: User,
sayHi() {
console.log('Hi,I am ' + this.name);
}
}
let cat = new User('cat');
console.dir(cat);
function createObj(obj, ...args) {
let constructor = Object.getPrototypeOf(cat).constructor;
return new constructor(...args)
}
let dog = createObj(cat, 'dog', 1);
console.dir(dog)
// dog.show()
dog.sayHi();
看一个实例吧
a instanceof A 判断A的原型是不是a原型链上的一份子
function A() { }
function B() { }
function C() { }
let c = new C();
B.prototype = c;
let b = new B();
A.prototype = b;
let a = new A();
console.log(a instanceof C); //true
Object.setPrototypeOf(b, a)//将a设置为b原型链上的一部分
let a = {};
let b = {};
Object.setPrototypeOf(b, a)//将a设置为b原型链上的一部分
console.log(b.__proto__ === a)//true
console.log(a.isPrototypeOf(b))//true
in 和 Object.hasOwnProperty()
let child = { name: 'datou' };
let father = { age: 30 }
Object.setPrototypeOf(child, father);
for (const key in child) {
if (father.hasOwnProperty(key)) {
console.log(father[key]) //30 遍历child的所有属性,因为father是child的原型,所以age属性能被访问
// father.hasOwnProperty(key)表示只有father对象有这个key属性,if结果为真。
}
}
call和apply的传参问题
他们两个都是用来改变函数中的this指向问题,当需要传递参数的时候
call可以传递多个参数
apply必须以数组的形式传递多个参数
console.log(person.sayHobby.call(person1, 'swimming', 'hiking')); // I'm Coy, I like swimming and hiking.
console.log(person.sayHobby.apply(person1, ['swimming', 'hiking'])); // I'm Coy, I like swimming and hiking.
baijiahao.baidu.com/s?id=162350…(这篇文章讲的非常清楚)
利用原型借鸡生蛋
let a = {
data: [1, 2, 5, 2, 3, 333, 5, 56]
}
Object.setPrototypeOf(a, {
max(data) {
console.log(data)
return data.sort((a, b) => b - a)[0];
}
})
let b = {
lesson: { js: 99, css: 98, html: 90 },
get data() {
return Object.values(b.lesson)
}
}
console.log(a.max(a.data))
console.log(a.max.call(null, (b.data)))
优化版借鸡生蛋,直接改变this指向,使用Math.max(), 这个函数接收多个数值
let a = {
data: [1, 2, 3, 4, 22, 11, 21]
}
let b = {
lesson: { js: 99, css: 98, html: 90 },
// get data() {
// return Object.values(b.lesson)
// }
}
console.log(Math.max(...[1, 22, 12, 32, 21]))
console.log(Math.max.apply({}, a.data))
// Object.values(b.lesson)给的是一个数组[99,98,90], apply传值必须是数组,传过去就不是数组了
console.log(Math.max.apply({}, Object.values(b.lesson)))
利用数组的filter方法和改变this指向来操作dom元素
<body>
<div class="one">one</div>
<div class="two">two</div>
<div id="three">three</div>
<div class="four">four</div>
<script>
let btns = document.querySelectorAll('div');
btns = [].filter.call(btns, item => {
return item.hasAttribute('class');
})
console.log(...btns)
</script>
</body>
正确的定义构造函数,即减小分配内存,利用原型
- 不恰当的定义构造函数 lisi 和zs两个对象会分别存储show() ,这个方法,造成了内存的浪费。
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
}
}
let lisi = new User('lisi');
let zs = new User('zs');
console.dir(lisi);
console.dir(zs)
- 恰当的使用原型
function User(name) {
this.name = name;
}
// User.prototype.show = function () {
// console.log(this.name);
// }
User.prototype = {
constructor: User,
show() {
console.log(this.name);
}
}
let lisi = new User('lisi');
let zs = new User('zs');
console.dir(lisi);
console.dir(zs)
Object.create()
- 它是一种为一个对象设置原型链 ,他创造了一个新的空间来指向目标原型,而使用对象指向这个新的空间(一个实例对象),新的空间指向目标原型(第二段代码可以解释)
let User = {
name: 'why',
show() {
console.log(this.name)
}
}
// let a = Object.create(b),将b设置为a的原型,第二个参数是设置a对象的属性
let hd = Object.create(User, {
name: {
value: 'wy'
}
})
hd = { name: 'cat' }
hd.show()
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
}
}
function Admin(name) {
this.name = name;
}
User.prototype.sayHi = function () {
console.log('Hi! ' + this.name);
}
let hd = new User('why');
hd.sayHi()
// User.prototype = Admin.prototype;
// console.log(User.prototype === Admin.prototype) // true
User.prototype = Object.create(Admin.prototype)
console.log(User.prototype.__proto__ === Admin.prototype) //true
let hd1 = new User('wy');
hd1.sayHi()//报错
这个例子可以更近一步体会二者的差别
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
}
}
function Admin(name) {
this.name = name;
}
let hd = new User('why');
// User.prototype.__proto__ = Admin.prototype;如果将这行代码与下面一行进行替换,则sayHi方法正常使用
User.prototype = Object.create(Admin.prototype)//报错是因为User.prototype代表的是一块新开辟的空间,
//而hd这个实例的原型还是原来的旧空间
User.prototype.sayHi = function () {
console.log('Hi! ' + this.name);
}
hd.sayHi();
hd.proto
- 那么怎么获取对象的原型呢,通过hd.__proto__既可以设置也可以获取
let User = {
name: 'why',
show() {
console.log(this.name)
}
}
hd = { name: 'cat' }
hd.__proto__ = User;
console.log(hd.__proto__)//{name: 'why', show: ƒ}这是一个对象
hd.show()
setPrototypeOf,getPrototypeOf
- setPrototypeOf设置,getPrototypeOf获取原型
let User = {
name: 'why',
show() {
console.log(this.name)
}
}
hd = { name: 'cat' }
Object.setPrototypeOf(hd, User);
console.log(Object.getPrototypeOf(hd))//{name: 'why', show: ƒ}效果与
// hd.__proto__ = User; console.log(hd.__proto__)相同
hd.show()
hd.__proto__原来是属性访问器
function User(name) {
this.name = name;
this.show = function () {
console.log('111')
}
}
let hd = new User('why')
//使用这种赋值原型的方式,是不会覆盖掉原生的原型的,如果show1变成show,hd.show()输出的仍然是111
hd.__proto__ = {
show1() {
console.log('222')
}
}
//再次使用这种方式赋值给一个对象,会把之前使用hd.__proto__=这种方式赋值的对象全部覆盖,无论属性方法名是否相同
hd.__proto__ = {
show1() {
console.log(333)
}
}
//hd.__proto__= 赋值的是一个对象才生效,数字无效,所以 hd.show2()//333 仍然可以使用
hd.__proto__ = 99;
hd.show()//111
hd.show2()//333
hd.show()//报错
- hd.__proto__原来是属性访问器不接受非对象赋值js原理
let hd = {
name: {},
get proto() {
return this.name;
},
set proto(value) {
if (value instanceof Object) {
this.name = value
}
}
}
hd.proto = { view: function () { } }
hd.proto = '99';
console.log(hd.proto) //{view: ƒ}
- 严格意义上说,__proto__不是对象的属性,是一个getter,setter
- 那如果我就是那设置一个__proto__这种属性名的属性怎么办呢?
- hd. _proto__指向对象的原型,而他的原型上有一个_proto__的属性访问器,那就让hd.__proto__不指向这个本来的原型
let a = {};
Object.setPrototypeOf(a, null);
// a = Object.create(null) //二者都可以改变原型指向
a.__proto__ = 111
console.dir(a.__proto__)
constructor
- constructor是原型上的一个属性,他指向对象。所以我们可以通过构造函数的实例找到构造函数
function User() {
}
let hd = new User();
console.log(hd.__proto__.constructor === User) //true
Object.getOwnPropertyDescriptors(User.prototype) 获取对象原型的属性特征
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
}
}
function Admin(name) {
this.name = name;
}
let hd = new User('why');
// User.prototype.__proto__ = Admin.prototype;如果将这行代码与下面一行进行替换,则sayHi方法正常使用
User.prototype = Object.create(Admin.prototype)//报错是因为User.prototype代表的是一块新开辟的空间,而hd这个实例的原型还是原来的旧空间
User.prototype.sayHi = function () {
console.log('Hi! ' + this.name);
}
User.prototype.constructor = User;
// 手动将User.property这个对象的constructor属性的enumerable属性值改为false,那么constructor就不可以被遍历了
Object.defineProperty(User.prototype, 'constructor', {
value: User,
enumerable: false
})
let a = new User;
for (const key in a) {
console.log(key)
}
console.log(Object.getOwnPropertyDescriptors(User.prototype));
hd.sayHi();
\