陌上寒

陌上寒个人博客

javascript面向对象与原型

《javascript面向对象与原型》
昨天我们讲了在面向对象中创建对象的几种方式

  • 工厂模式
  • 构造函数模式

工厂模式创建的对象,像工厂一样来创建对象,创建的每一个对象都是通过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);

《javascript面向对象与原型》
我们可以通过对象实例访问保存在原型中的值,但是我们不能通过对象实例重写原型中的值,强制的重写也可以

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);

《javascript面向对象与原型》
黄药师在实例中,欧阳锋在原型中,
我们访问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,
当在实例对象上添加引用类型时要格外小心
今天就到这里,明天不见不散
收集整理了一些电子书,有需要的,在公众号后台回复“电子书”即可领取
《javascript面向对象与原型》

发表评论

电子邮件地址不会被公开。 必填项已用*标注