资讯专栏INFORMATION COLUMN

js面向对象浅谈(二)

Yuanf / 953人阅读

摘要:我们通过这个构造函数为原型对象添加其他方法和属性。这个属性存在与实例与构造函数的原型对象上直接,而不存在于实例与构造函数之间。李小花班花张全蛋张全蛋李小花李小花我们在遍历对象的的属性的时候,经常需要判断属性是否来自于对象的原型还是属性。

引言

上面说了创建对象有字面量方式和工厂模式还有构造函数模式,结果发现他们都各自有缺点,所以下面再给大家介绍几种创建对象的方式,争取能找到一种无痛的模式?。

原型模式

下面会有一段非常晦涩难懂的内容,大家跟紧了别翻车。
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针(地址),指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

function Person () {

}
Person.prototype.name = "李小花";
Person.prototype.age = "60";
Person.prototype.sayName = function () {
    console.log(this.name);
};

var person1 = new Person();
person1.sayName();  //李小花

var person2 = new Person();
person2.sayName();  //李小花

Person 的prototype 属性中,构造函数,构造函数是空函数。新对象上的属性和实例是共享的。现在我们需要做一件事,就是理解原型对象。

理解原型对象

无论什么时候,只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,所有的原型都会获得一个constructor属性,指向prototype属性所在函数的指针。我们通过这个构造函数为原型对象添加其他方法和属性。创建对象后,其原型对象默认只有constructor属性。当构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型。虽然在脚本中没有标准的方式访问[[Prototype]],但除IE外都支持一个叫_proto_。这个属性存在与实例与构造函数的原型对象上直接,而不存在于实例与构造函数之间。
大家是不是看晕了?,我下面用一张图展示一下各个之间的对应关心,看好了,我要展示我高超的画图神功了。

灵魂图片已经展示,相信大家已经看懂了吧。需要注意的是,虽然这两个实例都不包含属性和方法 ,但我们可以在实例上调用原型上的方法,这是通过查找对象属性的过程来实现的。
扩展一下,既然js无法实例的的[[Prototype]],但是可以通过isPrototype()方法来确定这层关系。

console.log(Person.prototype.isPrototypeOf(person1)); // true

在es5中新增了一个方法,叫Object.getPrototype(),这个方法返回[[Prototype]]的值,例如,

console.log(Object.getPrototypeof(person1) == Person.prototype)  //true
console.log(Object.getPrototypeof(person1).name) //李小花

但是同样的,IE8以下的浏览器都不支持这个属性。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是给定名字的属性,搜索首先从实例本身开始,如果在实例本身找到了这个属性,则返回该属性的值,如果没有找到,则继续指针指向的原型对象,如果找到,则返回这个属性的值。

虽然可以通过对象实例访问保存在原型中的值,但确不能通过对象重写原型中的值。如果我们在实例中添加一个属性,而该属性与实例原型中的一个属性同名,这个属性会屏蔽原型中的那个属性。

function Person () {

}

Person.prototype.name = "李小花";
Person.prototype.age = "29";
Person.prototype.job = "班花";
Person.prototype.sayName = function () {
    console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = "张全蛋";
console.log(person1.name); // 张全蛋
console.log(person2.name); // 李小花

当为对象添加一个属性时,这个属性会屏蔽原型对象的同名属性,添加这个属性只会阻止我们访问原型的那个属性,但不会修改那个属性,使用delete属性能够让我们访问原型中的属性。

function Person () {

}

Person.prototype.name = "李小花";
Person.prototype.name = "29";
Person.prototype.job = "班花";
Person.prototype.sayName = function () {
    console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = "张全蛋";
console.log(person1.name);  //  张全蛋
console.log(person2.name);  //  李小花

delete person1.name

console.log(person1.name); //  李小花

我们在遍历对象的的属性的时候,经常需要判断属性是否来自于对象的原型还是属性。

function Person () {

}

Person.prototype.name = "李小花";
Person.prototype.name = "29";
Person.prototype.job = "班花";
Person.prototype.sayName = function () {
    console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name")); // false

person1.name = "张全蛋";

console.log(person1.hasOwnProperty("name")); // true
console.log(person1.name);  //  张全蛋
console.log(person2.name);  //  李小花

delete person1.name

console.log(person1.name); //  李小花

我们有一个神器,hasOwnProperty方法,只有当属性是实例的属性时,才会返回true。

原型操作符中的in方法

我上面介绍了怎么通过hasOwnProperty方法判断属性是否来自对象属性,那怎么判断属性来自原型链呢?
下面隆重介绍 in操作符,不过属性来自属性还是来自原型链的属性,都会返回true,我们通过这个属性结合hasOwnProperty方法判断属性是否来自于原型链。

function hasPrototypeproperty(obj, name) {
    return !object.hasOwnProperty(name) && (name in obj)
}
function Person () {

}

Person.prototype.name = "李小花";
Person.prototype.name = "29";
Person.prototype.job = "班花";
Person.prototype.sayName = function () {
    console.log(this.name);
};

var person1 = new Person();

console.log(hasPrototypeproperty(person, "name")); // true


person1.name = "张全蛋";

console.log(hasPrototypeproperty(person, "name")); // false

在使用for-in循环时,返回的是能够通过对象访问的,可枚举的属性,包括实例中的属性,也包含存在于原型中的属性,屏蔽原型中不可枚举的属性也会在for-in中返回。可以通过es5 中的Object.keys方法,返回一个包含所有可枚举属性的字符串数组。

function Person () {

}

Person.prototype.name = "李小花";
Person.prototype.age = "29";
Person.prototype.job = "班花";
Person.prototype.sayName = function () {
    console.log(this.name);
};


var keys = Object.keys(Person.prototype);

console.log(keys); //name age job sayName

var p1 = new Person();

p1.name = "全蛋";
p1.age = 20;
var p1keys = Object.keys(p1);

console.log(p1keys); // name,age

很多时候我们不想那么麻烦的写,所以会像下面这样简便的申明原型模式。

function Person () {

}

Person.prototype = {
    name: "李小花",
    age: 30,
    job: "班花",
    sayName: function () {
        console.log(this.name)
    }
}


Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
})  
function Person() {

}
var friend = new Person();

Person.prototype =  {
    constructor: Person,
    name: "李小花",
    age: 30,
    job: "班花",
    sayName: function () {
        console.log(this.name)
    }
};

friend.sayName(); //Uncaught TypeError: friend.sayName is not a function

这段代码大家是不是看蒙了,前面的我都白学了?难道不应该输出李小花吗?记得我们之前说的吗,创建构造函数会为实例添加一个指向最初原型的[[Prototype]]指针,这段代码修改了切断了构造函数与原型的联系,所以请记住,实例中的指针仅指向原型,而不指向构造函数。
为了增强大家的理解,我必须从操旧业,为大家画一幅流畅图。

重写原型对象之前的

重写原型对象之后

原生对象的问题

那么原声对象有缺点吗,如你所愿,当然是有缺点的,那么原声对象有缺点吗,如你所愿,当然是有缺点的,原型中的很多实例是被很多实例共享的,这种对于函数很合适,基本值的属性
也是可以的,可以通过在实例中添加一个属性,隐藏原型中的对应属性,对于包含引用类型的值的属性来说,问题就比较突出了。

  function Person () {

}
Person.prototype = {
    constructor: Person,
    name: "李小花",
    friends: ["全蛋", "大队长"]
}

var person1 = new Person();
var person2 = new Person();

person1.friends.push("狗蛋");

console.log(person1.friends) // ["全蛋", "大队长", "狗蛋"]
console.log(person2.friends)  // ["全蛋", "大队长", "狗蛋"]

person1.friends = [1, 2, 3] 

console.log(person1.friends) // [1, 2, 3],切断了与原型的指针
console.log(person2.friends)  // ["全蛋", "大队长", "狗蛋"]

现在业内最广泛最认同的模式是组合使用构造函数模式和原型模式

function Person () {
    name: "李小花",
    friends: ["全蛋", "大队长"]
}
Person.prototype = {
    constructor: Person,
    sayName: function () {
        console.log(1)
    }
}

剩余的动态原型模式、寄生构造函数模式、稳妥构造函数模式因为很少用这里就不多介绍了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83503.html

相关文章

  • 浅谈PHP面向对象编程

    摘要:一面向对象编程基础实践通过对象的编程方式,可将实现生活中的一切事物以对象的形式表现出来。此时程序也将会报致命错误。属性不可访问或未定义,值判断对象中的属性不存在时,自动执行该函数。属性值未定义释放对象中的不存在的属性值时,自动执行该函数。 一、PHP面向对象编程基础实践 二、PHP面向对象高级编程实践 知识点:类的继承、方法重写、访问控制、static关键字、final关键字、数据访...

    DrizzleX 评论0 收藏0
  • js面向对象浅谈(三)

    摘要:还有一个问题,就是不能在创建子类性时,像父类型的构造函数传递参数。组合继承将原型链和借用构造函数组合到一起,发挥两者之长的一张继承模式,下面来看个例子。组合继承最大的问题是无论在什么情况下,都会调用两次父类型构造函数。 继承 继承是面向对象语言中特别重要的概念,js的继承主要是靠原型链实现的。 原型链!!! 看到我给标题打了三个叹号吗,这里真的很重要!这里真的很重要!这里真的很重要!j...

    awkj 评论0 收藏0
  • js面向对象浅谈(一)

    摘要:引言对于面向对象,相信大家一定不陌生。创建对象面向对象第一步是什么答创建对象。构造函数优于工厂模式也是在于它可以通过辨识出一类的对象。 引言 对于面向对象,相信大家一定不陌生。最近看了一些关于es6面向对象的知识,正好通过这篇文章把关于面向对象的东西给串起来分享给大家。 什么是对象 很多人会鄙视我,说你这篇文章是骗骗刚入行的小朋友的吧,什么是对象我还能不知道?骂我的吃瓜群众先冷静一下,...

    lastSeries 评论0 收藏0
  • 浅谈面向对象的javascript几个特性

    摘要:中的和是一门很灵活的语言,尤其是。即然是面向对象的编程语言,那也是不可或缺的。在中,永远指向的是他的调用者。定义是存在于实例化后对象的一个属性,并且指向原对象的属性。我们在扩展的时候,同时父类也会有对应的方法,这很显然是一个很严重的问题。 javascript中的this和new javascript是一门很灵活的语言,尤其是function。他即可以以面向过程的方式来用,比如: f...

    JayChen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<