js面向对象及原型继承学习笔记。

2019-04-15 admin

创建对象

虽然Object构造函数或对象字面量都可以用来创建单个对象,但是这些方式有明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用工厂模式的一种变体。

工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节。

function createPerson(name,age,job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName=function(){
        console.log(this.name)
    }
    return o;
}
var person1 = createPerson('Nicholas',29,'SoftWare');
var person2 = createPerson('gres',27,'Doctor');

函数cretePerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无数次的调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但是却没有解决对象识别问题。

构造函数模式

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name);
    }
}
var person1 = new Person('Nicholas',29,'Software');
var person2 = new Person('gres',24,'Docotor');

在这个例子中,Person()函数取代了createPerson()函数。不同之处:

1.没有显式的创建对象。
2.直接将属性和方法赋给了this对象。
3.没有return语句。

要创建Person实例必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:

1.创建一个对象。
2.将构造函数的作用域赋值给新的对象(因此this指向了这个新对象)。
3.执行构造函数中的代码。(为这个新对象添加属性)。
4.返回新的对象。

上面的例子:person1和person2分别保存着Person的一个不同的实例。 这两个对象都有一个constructor属性,该属性指向Person。

person1.constructor == Person//true
person2.constructor == Person//true

person1 instanceof Object;//true
person1 instanceof Person;//true

创建自定义的构造函数意味着将来可以将它的实例标示为一种特定的类型;而这正是构造模式胜过工厂模式的地方。(以这种方式定义的构造函数是定义在Global对象(在浏览器是window对象)中的)。 构造函数与其它函数的唯一区别,就在于调用它们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那他就可以作为构造函数;任何函数,如果不通过new运算符调用,那它就跟普通函数没有区别。 构造函数的问题:

使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。在上面的例子中,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。在js中函数也是对象,因此每定义一个函数,也就是实例化了一个对象。
function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayName = new Function(console.log(this.name))
}

从这个角度上看构造函数,更容易明白每个Perosn实例都包含一个不同的Function实例的本质。说明白些,以这种方式创建函数,会导致不同的作用域链和标识符解析,但是创建Function新实例的机制仍然相同。因此不同实例上的同名函数是不相等的。

console.log(person1.sayName==person2.sayName);//false.

然而,创建两个完成同样任务的Function实例没有必要;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此,可以向下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = sayName;
}
function sayName(){
    console.log(this.name)
}
var person1 = new Person('ll',24);
var person2 = new Person('kk',25);

原型模式

我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以有特定类型的所有实例共享的属性和方法。

function Person(){}
Person.prototype.name = 'll';
Person.prototype.age = 24;
Person.prototype.sayName=function(){
    console.log(this.name)
}
var person1 = new Person();
var person2 = new Person();

在此我们将sayName()方法和属性直接添加到Person的prototype属性中,构造函数变成了空函数。即使如此,也仍然可以通过调用构造函数来创建新的对象,而且新的对象还会具有相同的属性和方法。但与构造函数模式不同的是,新对象的属性和方法是由所有实例共享的。 理解原型对象:

无论什么时候,只要创建了一个新函数,就会根据一定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针。Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继续为原型对象创建其它属性和方法。
创建了自定义构造函数之后,其原型对象默认只会得到constructor属性;至于其它属性和方法都是从Object对象继承而来的。当调用构造函数的一个新实例后,该实例内部将包含一个指针,指向构造函数的原型对象。(__proto__);person1和person2都包含一个内部属性,该属性仅仅指向了Person.prototype,和构造函数没有直接的关系。
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性,搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,如果在原型对象中找到了该属性,则返回该属性的值。
function Person(){}
Person.prototype.name = 'll';
Person.prototype.age = 24;
Person.prototype.sayName=function(){
    console.log(this.name)
}
var person1 = new Person();
var person2 = new Person();
person1.name = 'kk';
console.log(person1.name)//kk-来自实例
console.log(person2.name)//ll来自原型

当对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。

原型模式的缺点:

原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性也说的过去,通过在实例上添加一个同名属性可以隐藏原型中对应的属性。然而对于包含引用类型值的属性来说,问题就比较突出了:
function Person(){}
Person.prototype={
    constructor:Person,
    name:'kk',
    age:24,
    friends:['ll','jj'],
    sayName:function(){
        console.log(this.name);
    }
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push('aa');
console.log(person1.friends);//ll jj aa
console.log(person2.friends);//ll jj aa
console.log(person1.friends==person2.friends);//true

修改person1.friends引用的数组,person2同样会修改。

组合使用构造函数和原型模式

创建自定义类型的最常见方式,就是组合使用构造函数和原型模式。构造函数模式用于定义实例属性,原型模式用于定义共享的属性和方法。另外这种模式还支持向构造函数穿参数。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.friends=['kk','ll'];
}
Person.prototype={
    constructor:Person,
    sayName:function(){
        console.log(this.name)
    }
}
var person1 = new Person('nnn',24);
var person2 = new Person('mmm',29);
person1.friends.push('aaa');
console.log(person1.friends);//kk ll aa
console.log(person2.friends);//kk ll 
console.log(person1.friends==person2.friends);//false
console.log(person1.sayName==person2.sayName);//true

[转载]原文链接:https://segmentfault.com/a/1190000018866282

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-61817.html

文章标题:js面向对象及原型继承学习笔记。

相关文章
Vue.js组件tab实现选项卡切换
本文实例为大家分享了vue插件tab选项卡的具体代码,供大家参考,具体内容如下 效果图: 代码如下: <!DOCTYPE html> <html lang="en"> <head> ...
2017-03-13
从2014年的发展来展望JS的未来将会如何
<font face="寰�杞�闆呴粦, Arial, sans-serif ">2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
破解前端面试(80% 应聘者不及格系列):从 闭包说起
不起眼的开始 招聘前端工程师,尤其是中高级前端工程师,扎实的 JS 基础绝对是必要条件,基础不扎实的工程师在面对前端开发中的各种问题时大概率会束手无策。在考察候选人 JS 基础的时候,我经常会提供下面这段代码,然后让候选人分析它实际运行的结...
2017-06-02
three.js实现围绕某物体旋转
话不多说,请看代码: 可以拖动右上角观察变化 <!DOCTYPE html> <html lang="en" style="width: 100%; height:100%;"&gt...
2017-02-17
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
jsdom 中文文档(纯翻译)
jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOM和 HTML 标准,用于在 nodejs 中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界...
2018-05-14
NodeJS参考手册pdf版
下载地址:Nodejs参考手册PDF版下载 ...
2015-11-12
Node.js学习(1)----HTTP服务器与客户端
Node.js 标准库提供了 http 模块,其中封装了一个高效的 HTTP 服务器和一个简易的HTTP 客户端。http.Server 是一个基于事件的 HTTP 服务器,它的核心由 Node.js 下层 C++部分实现,而接口由 Jav...
2015-11-12
使用jspdf生成pdf报表
由于前台html已经动态生成报表,而且,前台有一个功能,一个date range组件,当你拖动的时候,报表会在不提交到后台的情况下动态变化。 因此需要用到js生成生报表: 用到的组件: jquery.js jspdf.js canvg.js...
2017-03-25
Riot.js:不足1KB的MVP客户端框架
Riot.js是一款MVP(模型-视图-呈现)开源客户端框架,其最大的特点就是体积非常小,不足1KB,虽然体积小,但它可以帮助用户构建大规模的Web应用程序。 Riot.js是由Moot公司开发,目前最新版本为v0.9.2,遵循MIT开源许...
2016-03-11
回到顶部