共计 3730 个字符,预计需要花费 10 分钟才能阅读完成。
昨天我们讲了在面向对象中创建对象的几种方式
工厂模式
构造函数模式
工厂模式创建的对象,像工厂一样来创建对象,创建的每一个对象都是通过 new Object() 来创建的,原型直指 Object() 构造函数似乎不错,但有的时候我们需要对属性和方法进行修改,属性 vue 的同学应该都遇到过这种情况,我们需要声明一些全局变量,我们一般这么做
// 封装全局的 ajax 请求
Vue.prototype.$http = function (url, options) {
// 部分代码省略
return fetch(url, options)
}
// 注入 全局的 wxSDK
Vue.prototype.$wx = Wx
这时候就用到了原型我之前就用了大量篇幅讲过 javascript 的原型,这次遇到了面向对象,换个角度再次讨论
原型模式创建对象
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。也就是说,不用再构造函数中定义对象的实例信息,而是将这些属性和方法添加到原型对象中一个????:
function Hero() {}
Hero.prototype.name = “ 欧阳锋 ”
Hero.prototype.nickname = “ 西毒 ”
Hero.prototype.doSth = function () {
console.log(‘ 学习九阴真经 ’);
}
const hero1 = new Hero()
console.log(hero1.name); //=> 欧阳锋
console.log(hero1.nickname); //=> 西毒
hero1.doSth() //=> 学习九阴真经
const hero2 = new Hero()
console.log(hero2.name); //=> 欧阳锋
console.log(hero2.nickname); //=> 西毒
hero2.doSth() //=> 学习九阴真经
我们看下 hero1
console.log(hero1);
我们可以通过对象实例访问保存在原型中的值,但是我们不能通过对象实例重写原型中的值,强制的重写也可以
function Hero() {
}
Hero.prototype.name = “ 欧阳锋 ”
Hero.prototype.nickname = “ 西毒 ”
Hero.prototype.doSth = function () {
console.log(‘ 学习九阴真经 ’);
}
const hero1 = new Hero()
hero1.__proto__.name=’ 黄药师 ’
console.log(hero1.name);//=> 黄药师
const hero2 = new Hero()
console.log(hero2.name);//=> 黄药师
这么做就是修改了原型链,通过原型创建的其他实例也会同步更改,这样不是我们所希望的结果怎么办?
实例属性屏蔽同名的原型属性
在实例中添加属性,使这个属性和原型中的属性同名,这个属性会屏蔽掉(也可以理解为覆盖掉)原型中的那个属性继续???? 的????
function Hero() {
}
Hero.prototype.name = “ 欧阳锋 ”
Hero.prototype.nickname = “ 西毒 ”
Hero.prototype.doSth = function () {
console.log(‘ 学习九阴真经 ’);
}
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=” 黄药师 ”
console.log(hero1.name);//=> 黄药师
console.log(hero2.name);//=> 欧阳锋
我们看下 hero1
console.log(hero1);
黄药师在实例中,欧阳锋在原型中,我们访问 hero1.name,会先在实例上去搜索 name 属性,实例上存在 name 属性,就返回实例上的 name 属性,停止搜索。我们再去访问 hero2.name,同样的会在实例上进行搜索 name 属性,但是没搜到,继续顺着原型链进行搜索,搜到了,返回原型中的 name 属性。我们在原型中添加一个属性,这个属性会屏蔽掉原型中的同名属性,也就是说会阻止我们访问原型中的同名属性,但是不会修改,如果我们把这个属性设置为 null,会怎么样呢?
// 部分代码省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=null
console.log(hero1.name);//=>null(来自实例)
console.log(hero2.name);//=> 欧阳锋(来自原型)
所以说,就算设置为 null,结论是和上面是相同的,只要在实例上存在这个要访问的属性,就会停止搜索实例中的属性我不想要了怎么办,设置 null 也无效,使用 delete 删除
// 部分代码省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=null
delete(hero1.name)// 删除实例中的属性
console.log(hero1.name);//=> 欧阳锋(来自原型)
console.log(hero2.name);//=> 欧阳锋(来自原型)
判断属性属于实例还是原型
有个内置方法 hasOwnProperty() 用来判断属性是否来自于实例,属于实例则返回 true,否则 false
// 部分代码省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=’ 黄药师 ’
// console.log(hero1.name);
// console.log(hero2.name);
console.log(hero1.hasOwnProperty(‘name’));//=>true
console.log(hero2.hasOwnProperty(‘name’));//=>false
还有一个 in 操作符,如果对象能访问到指定属性就返回 true
// 部分代码省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=’ 黄药师 ’
console.log(‘name’ in hero1);//=>true
console.log(‘name’ in hero2);//=>true
我们将 in 和 hasOwnProperty() 结合在一起,封装一个方法用来判断属性来自原型还是实例
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name = ‘ 黄药师 ’
//true=> 来自原型
//false=> 来自实例
function attrFromProto(obj, attr) {
return !obj.hasOwnProperty(attr) && (attr in obj)
}
console.log(attrFromProto(hero1, ‘name’));//=>false(来自实例)
console.log(attrFromProto(hero2, ‘name’));//=>true(来自原型)
原型模式创建对象的简化版
???? 我们创建对象是这样的
function Hero() {
}
Hero.prototype.name = “ 欧阳锋 ”
Hero.prototype.nickname = “ 西毒 ”
Hero.prototype.doSth = function () {
console.log(‘ 学习九阴真经 ’);
}
我能重复的写 prototype,我们结合对象字面量简化一下
function Hero() {
}
Hero.prototype = {
name: “ 欧阳锋 ”,
nickname: “ 西毒 ”,
doSth: function () {
console.log(‘ 学习九阴真经 ’);
}
}
通过原型模式创建对象注意事项
在原型中添加公有方法是值得鼓励的,类似于我们在 vue 中添加全局 ajax,添加一下基本类型的变量也可以,但是当添加引用类型的属性时候问题就出现了
function Hero() {
}
Hero.prototype.name = “ 欧阳锋 ”
Hero.prototype.nickname = “ 西毒 ”
Hero.prototype.skill = [“ 灵蛇拳 ”, “ 神驼雪山掌 ”]
Hero.prototype.doSth = function () {
console.log(‘ 学习九阴真经 ’);
}
const hero1 = new Hero()
const hero2 = new Hero()
hero1.skill.push(‘ 灵蛇杖法 ’)
console.log(hero1.skill);//=>[“ 灵蛇拳 ”, “ 神驼雪山掌 ”, “ 灵蛇杖法 ”]
console.log(hero2.skill);//=>[“ 灵蛇拳 ”, “ 神驼雪山掌 ”, “ 灵蛇杖法 ”]
我只在 hero1 实例中 push 了“灵蛇杖法”,结果影响到了 hero2,当在实例对象上添加引用类型时要格外小心今天就到这里,明天不见不散收集整理了一些电子书,有需要的,在公众号后台回复“电子书”即可领取
原文链接