从创建对象开始

创建对象的简单方法就是:使用Object构造函数或者对象字面量。但这两个方法有个缺点:创建相同对象,要编写大量重复代码。

为了避免这个问题——>工厂模式:用函数封装以特定接口创建对象

function createPerson(name, age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
return this.name;
}
return o;
}
var p1 = createPerson("Tom", 23);
var p2 = createPerson("Joe", 20)
p1 instanceof createPerson //false

工厂模式:优点: 解决了创建多个相似对象问题   缺点:没有解决对象识别问题(不知道对象的类型)

为了解决对象识别问题——>构造函数模式:创建特定类型的对象

function Person(name, age){
this.name = name;
this.age = age;
this.sayName = function(){
return this.name;
}
}
var p1 = new Person("Tom", 23);
var p2 = new Person("Joe", 20);
p1 instanceof Person //true 检测对象类型
p1.constructor === Person //true 识别对象类型 p1.sayName === p2.sayName //false

构造函数模式:Person()与createPerson()   不同之处:没有显示创建对象、将属性方法值赋值给this、没有return 语句,还有就是函数名一个字母大写。

构造函数也是函数,那么函数用new执行会发生什么呢(重点)

1.创建一个空对象。

2.将构造函数的作用域赋值给新对象(this指向该对象, 同时还继承了该函数的原型)。

3.执行构造函数中的代码(添加属性和方法)

4.隐式的返回this(返回该对象, 如果没有显示的返回其他对象的话)

上面说了构造函数也是函数,只是使用了new 调用,如果不使用new就和普通函数一样

var wp = Person("Marry", 18);
window.sayName(); //"Marry"
wp.sayName(); //TypeError: Cannot read property 'sayName' of undefined
sayName() //"Marry"

构造函数解决了对象识别问题,但是缺点:正如前面(p1.sayName === p2.sayName //false )会导致不同作用域链和标识符解析

//构造函数中的
this.sayName = function(){
return this.name;
}
//等价于
this.sayName = new Function("return this.name;")

为了解决这个问题,我们可以把sayName函数放外面作为全局函数,但这方法缺点颇多,很不合适。于是...

出现了——>原型模式:函数有个prototype属性,指向一个对象,该对象可以包含由特定类型的所有实例共享的属性和方法,简称之prototype是p1,p2的原型对象。这个原型对象可以让所有实例共享它包含的方法和属性。

function Person(){}
Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
return this.name;
}
var p1 = new Person();
var p2 = new Person();
p1.sayName === p2.sayName; //true p1.name = "other";
p2.friends.push("D");
p1.sayName(); // "other" ---来自实例
p2.sayName(); // "Tom"; ---来自原型 p1.friends; //["A", "B", "C", "D"] ----引用类型
p2.friends; //["A", "B", "C", "D"] Person.prototype.isPrototypeOf(p1) //true
Object.getPrototypeOf(p1) === Person.prototype //true //in 通过对象能访问给定属性就返回true,不管实例还是原型
"name" in p1; //true
"name" in p2; //true

// hasOwnProperty() 只返回实例中的属性
p1.hasOwnProperty("name"); //true
p2.hasOwnProperty("name"); //false //Object.keys() 返回对象上所有可枚举实例属性
Object.keys(Person.prototype); //["name", "age", "friends", "sayName"]
Object.keys(p1); //["name"]

//Object.getOwnPropertyNames() 返回所有实例属性,无论是否可枚举
Object.getOwnPropertyNames(Person.prototype) //["constructor", "name", "age", "friends", "sayName"]
Object.getOwnPropertyNames(p1) //["name"]

说明:实例,构造函数,原型的关系:实例的[[Prototype]](__proto__)只指向原型、构造函数的prototype属性指向原型、原型的constructor指向构造函数。

in操作符:在<= IE8中 in不会枚举[[Enumerate]]为false的同名属性 ;在safari3中 in会枚举被隐藏的属性。 所以in慎用。

其中上述代码

Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
return this.name;
}
//可写成: ---相当于重写原型
Person.prototype = {
constructor: Person,
name = "Tom",
age = 20,
friends = ["A", "B", "C"],
sayName = function(){
return this.name;
}
} //支持ES5中,可去掉 上句 constructor: Person,添上如下代码:
Object.defineProperty(Person.prototype, "constructor", {
enumerate: false,
value: Person
});

原型模式:虽然解决了构造函数的缺点问题,但缺点: 1.默认情况下取相同的值;2.引用类型的实例被很多实例共享

为了避免这些问题——〉组合使用构造函数和原型模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性, 最大限度的节省了内存

function Person(name, age){
this.name = name;
this.age = age;
this.friends = ["A", "B", "C"];
}
Person.prototype = {
constructor: Person,
sayName: function(){
return this.name;
}
}
var p1 = new Person("Tom", 34);
var p2 = new Person("Joe", 21);
p1.friends.push("D");
p1.friends //["A", "B", "C", "D"]
p2.friends //["A", "B", "C"]
p1.sayName === p2.sayName; //true

组合模式:优点:集两模式优点于一身,极好!因此该模式使用最广泛、认知度最高。

东西一好,就有人挑骨头,其他oo语言开发经验者看到独立的构造函数与原型会觉得很困惑,于是...

出现了——>动态原型模式:将所有信息封装在构造函数中

function Person(name, age){
this.name = name;
this.age = age;
this.friends = ["A", "B", "C"];
if (typeof this.sayName != "function") {
Person.prototype.sayName = function(){
return this.name;
}
}
} //创建第一个实例才会执行,此后,原型完成初始化
var p1 = new Person("Tom", 23);
p1.sayName(); //"Tom"

至此,如果都不适用的话...

出现了——〉寄生构造函数模式:创建一个函数,作用仅仅是封装创建对象的代码,然后再返回新创建的对象

function Person(name, age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
return this.name;
}
return o;
}
var p1 = new Person("Tom", 23);
p1.sayName();

该模式准确的说与工厂模式一模一样。只是把该函数当构造函数调用(使用new)而与构造函数模式相似,又有点不同,这不同之处就是:显示的创建对象,重写了返回值。

既然如此,这个模式的优势在哪里呢?对于构造函数模式而言,它可以new一个除object类型之外的其他类型——在不扩展原生构造函数的情况下自定义一个扩展型的构造函数。

function SpecialArray(){
var values = new Array();
values.push.apply(values, arguments);
values.toPipedString = function(){
return this.join("|");
};
return values;
}
var friends = new SpecialArray("A", "B", "C");
friends.toPipedString(); //"A|B|C"
friends instanceof SpecialArray //false
friends instanceof Array //true

说明:构造函数返回的对象与构造函数或者构造函数的原型之间没有任何关系,也就是说构造函数外面创建或者里面创建是没有区别的,(只是寄生在构造函数中)因此无法用instanceof来识别对象。

还有一种创建对象的方法:——>稳妥构造函数模式:没有公共属性,不使用this、new,适合安全环境下使用

function Person(name, age){
}
function Person(name){
var o = new Object();
o.sayName = function(){
return name;
}
return o;
}
var p1 = Person("Tom");
var p2 = Person("Joe");
p1.name //undefined
p2.name //undefined
p1.sayName() //"Tom"
p2.sayName() //"Joe" 传入构造函数的name之恩那个通过sayName()访问

稳妥构造函数模式与寄生构造函数模式类似:创建的对象与构造函数之间没有任何关系。所以无法用instanceof来识别对象。

该博客与js高级程序设计内容相似,加之自己的理解与总结,不对之处欢迎指出。详情可看js高级程序设计。

js继承——从创建对象开始的更多相关文章

  1. 浅谈JS继承

    今天呢,我们来谈谈继承,它也是JS语言中的一大重点,一般什么时候我们会用继承呢,比如有两个拖拽的面板,两个功能基本一致,只是第二个面板多了一些不同的东西,这个时候,我们就会希望,要是第二个直接能继承第 ...

  2. 老生常谈--Js继承小结

    一直以来,对Js的继承有所认识,但是认识不全面,没什么深刻印象.于是,经常性的浪费很多时间重新看博文学习继承,今天工作不是特别忙,有幸看到了http://www.slideshare.net/stoy ...

  3. Js继承小结

    Js继承小结 一直以来,对Js的继承有所认识,但是认识不全面,没什么深刻印象.于是,经常性的浪费很多时间重新看博文学习继承,今天工作不是特别忙,有幸看到了http://www.slideshare.n ...

  4. js继承的常用方法

    写在前面的话:这篇博客不适合对面向对象一无所知的人,如果你连_proto_.prototype...都不是很了解的话,建议还是先去了解一下JavaScript面向对象的基础知识,毕竟胖子不是一口吃成的 ...

  5. JS继承的一些见解

    JS继承的一些见解 js在es6之前的继承是五花八门的.而且要在项目中灵活运用面向对象写法也是有点别扭,更多的时候还是觉得面向过程的写法更为简单,效率也高.久而久之对js的继承每隔一段时间就会理解出现 ...

  6. JS继承方式详解

    js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现 ...

  7. JS继承的原理、方式和应用

    概要: 一.继承的原理 二.继承的几种方式 三.继承的应用场景 什么是继承? 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展.继承的过程,就是从一般到特殊的过程.要了解JS继承必须首先要了解 ...

  8. js继承的方式及其优缺点

    js继承方法 前因:ECMAScript不支持接口继承,只支持实现继承 一.原型链 概念:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,让 ...

  9. JS继承(简单理解版)

    童鞋们,我们今天聊聊js的继承,关于继承,平时开发基本用不到,但是面试没有不考的,我就想问,这是人干的事吗? 好吧,迫于社会主义核心价值观,我们今天就来简单说一说js的继承,谁让它是面向对象编程很重要 ...

随机推荐

  1. 用js实现图片连播和联级菜单的实现

    <!DOCTYPE html> <html> <head> <title>图片轮播</title> <style> div{ b ...

  2. Bootstrap开发模板

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...

  3. spring源码分析---IOC(1)

    我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...

  4. 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第三部分

    前面两篇介绍了SOM的基本概念和算法,第一部分,第二部分,本篇具体展开一下应用中的一些trick设定. SOM设计细节 输出层设计 输出层神经元数量设定和训练集样本的类别数相关,但是实际中我们往往不能 ...

  5. jquery文档

    http://jquery.cuishifeng.cn/selected_1.html

  6. layer弹出层的关闭问题

    就是在执行添加或修改的时候,需要将数据提交到后台进行处理,这时候添加成功之后最理想的状态是关闭弹出层并且刷新列表的数据信息,之前一直想实现这样,可一直没有成功,今天决定好好弄一弄,在仔细看过layer ...

  7. 在 Ubuntu 系统安装 Redi laravel 5.2 引入第三方类

    composer 安装类依赖包 很受用 也很方便 但是要是一个有一定规模的公司技术团队 因为要照顾大局 还是引入类好些 下面是引入类的方法 1.首先在app目录下创建一个新的文件夹,命名Tools(可 ...

  8. 【.NET】学习SQLite(1)

    前沿 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存 ...

  9. shell-命令行参数(转)

    命令行参数 (转自http://c.biancheng.net/cpp/view/2739.html) 特殊变量列表 变量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数.n 是一个数字, ...

  10. 尝试php命令行脚本多进程并发执行

    php不支持多线程,但是我们可以把问题转换成“多进程”来解决.由于php中的pcntl_fork只有unix平台才可以使用,所以本文尝试使用popen来替代.  下面是一个例子:  被并行调用的子程序 ...