创建对象

var box = new Object();//创建对象
box.name = 'Lee'; //添加属性
box.age = 100;
box.run = function(){
return this.name + this.age + "运行中"; //this 表示当前作用域下对象
} // this 表示new Object()实例出来的那个对象
alert(box.run());

这就是创建对象最基本的方法,但是有个缺点,想创建一个类似的对象,就会产生大量的代码。

工厂模式

为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。

function createObject(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function(){
return this.name+this.age+"岁年龄";
}
return obj;
} var box1 = createObject('Lee',20);
var box2 = createObject('Jack',30);
console.log(box1.run());
console.log(box2.run());

工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清他们到底是哪个对象的实例。

alert(typeof box1); //Object
alert(box1 instanceof Object);//true

构造函数

ECAMScript中采用构造函数(构造方法)可用来创建特定的对象。类似于Object对象。

//构造函数
function Box(name,age){
this.name = name;
this.age = age;
this.run = function(){
return this.name + this.age +"运行中...";
};
}; var box1 = new Box('Lee',100);
var box2 = new Box('Jack',200); console.log(box1.run());
console.log(box2.run());

使用构造函数的方法,即解决了重复实例化的问题,又解决了对象识别的问题,但问题是,这里并没有new Object(),为什么可以实例化Box(),这个是哪里来的呢?

使用了构造函数的方法,和使用工厂模式的方法他们不同之处如下:

  • 1.构造函数方法没有显示的创建对象(new Objectt()),但它在后台自动var obj = new Object();
  • 2.直接将属性和方法赋值给this对象,this就相当于obj;
  • 3.没有return语句,不需要返回对象引用,它是在后台自动返回的。
//构造函数
function Box(name,age){
this.name = name;
this.age = age;
this.run = function(){
return this.name + this.age +"运行中...";
};
}; function Dack(name,age){
this.name = name;
this.age = age;
this.run = function(){
return this.name + this.age +"运行中...";
};
}; var box1 = new Box('Lee',100);
var box2 = new Box('Jack',200);
var box3 = new Dack('MrLee',300); console.log(box1.run());
console.log(box2.run());
console.log(box3.run()); //解决了对象识别问题
console.log(box1 instanceof Box); //true
console.log(box2 instanceof Box); //true
console.log(box3 instanceof Box); //false
console.log(box3 instanceof Dack);//true

对象冒充:使用call()方法

var o= new Object();
Box.call(o,'Lee',100);
console.log(o.run());

看下一个问题:

 var box1 = new Box('Lee',100); //实例化后地址为1
var box2 = new Box('Lee',100); //实例化后地址为2 console.log(box1.name == box2.name); //true
console.log(box1.age == box2.age); //true
console.log(box1.run() == box2.run());//true //构造函数体内的方法的值是相当的
console.log(box1.run == box2.run); //false //因为他们比较的是引用地址

上面的代码运行说明引用地址不一样,那么构造函数内的方法也可以这样写:

this.run = new Function("return this.name + this.age +'运行'") 

如何让他们的引用地址一样,下面代码:

function Box(name,age){
this.name = name;
this.age = age;
this.run = run;
}; function run(){
return this.name +this.age+"运行中...";
}
var box1 = new Box('Lee',100); //实例化后地址为1
var box2 = new Box('Lee',100); //实例化后地址为2
console.log(box1.run == box2.run); //true //因为他们比较的是引用地址

把构造函数内部的方法通过全局来实现引用地址一致。
虽然使用了全局函数run()来解决了保证引用地址一致的问题,但是这种方式又带来了一个新的问题,全局中的this在对象调用的时候是Box本身,而当普通函数调用的时候,this又代表window。

原型

function Box(){
//构造函数函数体内什么都没有,这里如有过,叫做实例属性,实例方法
} Box.prototype.name="Lee"; //原型属性
Box.prototype.age=100;
Box.prototype.run=function(){ //原型方法
return this.name+this.age+"运行中...";
} var box1=new Box();
var box2=new Box();
console.log(box1.run == box2.run); //true
console.log(box1.prototype);//这个属性是一个对象,访问不到
console.log(box1.__proto__);//这个属性是一个指针指向prototype原型对象。

如果是实例方法,不同的实例化,他们的方法地址是不一样的,是唯一的。
如果是原型方法,那么他们的地址是共享的,大家都一样。

console.log(box1.constructor); //构造属性,可以获取构造函数 

PS:IE浏览器在脚本访问__proto__会不能识别,火狐和谷歌及其他某些浏览器能识别。虽然可以输出,但是无法获取内部信息。

判断一个对象是否指向该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。

console.log(Box.prototype.isPrototypeOf(box1)); //true //只要实例化对象,即都会指向

原型模式的执行流程:

  • 1.先查找构造函数实例里的属性或方法,如果有,立刻返回;
  • 2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回。

如何判断属性时构造函数的实例里,还是原型里?可以使用hasOwnProperty()函数来验证:

console.log(box1.hasOwnProperty('name'));//如果实例里有返回true,否则返回false  

如何判断属性是原型里的?

function Box(){

} 

Box.prototype.name="Lee"; //原型属性
Box.prototype.age=100;
Box.prototype.run=function(){ //原型方法
return this.name+this.age+"运行中...";
} function isProperty(object,property){
return !object.hasOwnProperty(property) && (property in object);
}
var box1=new Box();
console.log(isProperty(box1,'name'));

为了让属性和方法更好的体现封装的效果,并且减少不必要的输入,原型的创建可以使用字面量的方式

使用字面量的方式创建原型对象,这里的{}就是对象,是object,new Object就相当于{}

function Box(){} 

Box.prototype={
name:'Lee',
age:100,
run:function(){
return this.name+this.age+"运行中...";
}
} var box = new Box();
console.log(box.constructor == Box); //false

字面量创建的方式使用constructor属性不会指向实例,而会指向Object,构造函数创建的方式则相反。
这里的Box.prototype={}就相当于创建了一个新的对象,所以 box.constructor是Object。
如何让box.constructor指向Box呢?

function Box(){} 

Box.prototype={
constructor:Box,//直接强制指向即可
name:'Lee',
age:100,
run:function(){
return this.name+this.age+"运行中...";
}
} var box = new Box();
console.log(box.constructor == Box); //true

重写原型,不会保留之前原型的任何信息,把原来的原型对象和构造函数对象的实例切断了。

function Box(){} 

Box.prototype={
constructor:Box,
name:'Lee',
age:100,
run:function(){
return this.name+this.age+"运行中...";
}
} //重写原型
Box.prototype={
age:200
}
var box = new Box();
console.log(box.name); //undefined

查看sort是否是Array原型对象里的方法

alert(Array.prototype.sort);

在如下 判断String原型对象里是否有substring方法

alert(String.prototype.substring);

给String 添加addstring方法:

String.prototype.addstring=function(){
return this+',被添加了!';
}
var box="Lee";
console.log(box.addstring());

注:原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。

原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题:

function Box(){}

Box.prototype={
constructor:Box,
name:'Lee',
age:100,
family:['哥哥','姐姐','妹妹'],
run:function(){
return this.name+this.age+"运行中...";
}
}; var box1 = new Box();
console.log(box1.family); //'哥哥','姐姐','妹妹'
box1.family.push("弟弟");
console.log(box1.family);//'哥哥','姐姐','妹妹','弟弟' var box2 = new Box();
console.log(box2.family);//'哥哥','姐姐','妹妹','弟弟'

从上面代码可以看出,在第一个实例修改后引用类型,保持了共享。box2.family共享了box1添加后的引用类型的原型。

为了解决构造传参和共享问题,可以组合构造函数+原型模式:

function Box(name,age){  //保持独立的用构造函数
this.name=name;
this.age=age;
this.family=['哥哥','姐姐','妹妹'];
} Box.prototype={ //保持共享的用原型
constructor:Box,
run:function(){
return this.name+this.age+"运行中...";
}
} var box1 = new Box('Lee',100);
console.log(box1.family); //'哥哥','姐姐','妹妹'
box1.family.push("弟弟");
console.log(box1.family);//'哥哥','姐姐','妹妹','弟弟' var box2 = new Box('Jack',200);
console.log(box2.family); //'哥哥','姐姐','妹妹' //引用类型没有使用原型,所以没有共享

动态原型模式

//把原型封装到构造函数里
function Box(name,age){
this.name=name;
this.age=age;
this.family=['哥哥','姐姐','妹妹']; console.log('原型初始化开始'); //执行了两次
Box.prototype.run=function(){
return this.name+this.age+"运行中...";
}
console.log('原型初始化结束'); //执行了两次
} //原型的初始化,只要第一次初始化就可以了,没必要每次构造函数实例化的时候都初始化
var box1 = new Box('Lee',100);
var box2 = new Box('Jack',200);

为了只让第一次初始化,那么就判断

function Box(name,age){
this.name=name;
this.age=age;
this.family=['哥哥','姐姐','妹妹']; if(typeof this.run!='function'){
console.log('原型初始化开始'); //执行了一次次
Box.prototype.run=function(){
return this.name+this.age+"运行中...";
};
console.log('原型初始化结束'); //执行了一次
}
} //原型的初始化,只要第一次初始化就可以了,没必要每次构造函数实例化的时候都初始化
var box1 = new Box('Lee',100);
var box2 = new Box('Jack',200);

寄生构造函数
如果以上都不能满足需要,可以使用一下寄生构造函数。
寄生构造函数=工厂模式+构造函数

function Box(name,age){
var obj = new Object();
obj.name=name;
obj.age=age;
obj.run=function(){
return this.name+this.age+"运行中...";
}
return obj;
} var box1 = new Box('Lee',100);
var box2 = new Box('Jack',200);

稳妥构造函数

在一些安全的环境中,比如禁止使用this和new,这里的this是构造函数里不使用的this,这里的new是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。

function Box(name,age){
var obj = new Object();
obj.name=name;
obj.age=age;
obj.run=function(){
return this.name+this.age+"运行中...";
}
return obj;
} var box1 = Box('Lee',100);
var box2 = Box('Jack',200);

继承

继承是面向对象中一个比较核心的概念。其它正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

function Box(){
this.name="Lee";
} function Jack(){
this.age=100;
} Jack.prototype = new Box(); var jack = new Jack();
console.log(jack.name); //Lee

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术解决这两个问题。

function Box(name){
this.name=name;
} Box.prototype.age=200; function Jack(name){
Box.call(this,name);
} var jack = new Jack('Lee'); console.log(jack.name);//Lee
console.log(jack.age);//undefined

但是上面的代码可以看出,对象冒充没有继承原型链上的age属性。所以要继承Box的原型,就出现下面的组合继承。
组合继承即是原型链+借用构造函数的模式

function Box(name){
this.name=name;
} Box.prototype.age=200; function Jack(name){
Box.call(this,name);
} Jack.prototype = new Box(); var jack = new Jack('Lee'); console.log(jack.name);//Lee
console.log(jack.age);//

原型式继承

//临时中转函数
function obj(o){
function F(){};
F.prototype = o;
return new F();
} //这是字面量的声明方式,相当于var box = new Box();
var box={
name:'Lee',
age:100,
family:['哥哥','姐姐','妹妹']
}; var box1 = obj(box);
console.log(box1.family);//'哥哥','姐姐','妹妹'
box1.family.push('弟弟');
console.log(box1.family);//'哥哥','姐姐','妹妹','弟弟' var box2 = obj(box);
console.log(box2.family);//'哥哥','姐姐','妹妹','弟弟'

存在的问题就是引用类型共享了。

寄生式继承
把原型式与工厂模式结合起来。

//临时中转函数
function obj(o){
function F(){};
F.prototype = o;
return new F();
} //寄生函数
function create(o){
var f=obj(o);
f.run=function(){
return this.name+"方法";
}
return f;
} //这是字面量的声明方式,相当于var box = new Box();
var box={
name:'Lee',
age:100,
family:['哥哥','姐姐','妹妹']
}; var box1 = create(box);
console.log(box1.run());

寄生组合继承

//临时中转函数
function obj(o){
function F(){};
F.prototype = o;
return new F();
} //寄生函数
function create(box,desk){
var f=obj(box.prototype);
f.constructor=desk; //调整原型构造指针
desk.prototype=f;
} function Box(name,age){
this.name=name;
this.age=age;
} Box.prototype.run=function(){
return this.name+this.age+"运行中...";
} function Desk(name,age){
Box.call(this,name,age); //对象冒充
} //通过寄生组合继承来实现继承
create(Box,Desk); //这句话用来替代Desk.prototype = new Box(); var desk = new Desk('Lee',100);
console.log(desk.run());

js面向对象与原型的更多相关文章

  1. JS面向对象之原型

    面向对象之原型 为什么要使用原型 由于 js 是解释执行的语言, 那么在代码中出现的函数与对象, 如果重复执行, 那么会创建多个副本, 消耗更多的内存, 从而降低性能 传统构造函数的问题 functi ...

  2. JS面向对象,原型,继承

    ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScript ...

  3. JS面向对象之原型链

      对象的原型链 只要是对象就有原型 原型也是对象 只要是对象就有原型, 并且原型也是对象, 因此只要定义了一个对象, 那么就可以找到他的原型, 如此反复, 就可以构成一个对象的序列, 这个结构就被成 ...

  4. 从零开始的全栈工程师——JS面向对象( 原型 this 继承)

    一.生成对象的方式 ①单例模式(字面量定义)var obj = {} ②类的实例 var obj = new Object( ) ③工厂模式 ④构造函数:扮演三种角色 普通函数 普通对象 类 工厂模式 ...

  5. JS面向对象——动态原型模型、寄生构造模型

    动态原型模型 组合使用构造函数模型和原型模型,使得OO语言程序员在看到独立的构造函数和原型时很困惑.动态原型模型致力于解决该问题,它把所有的信息封装在构造函数中,通过在构造函数中初始化原型(仅在必要情 ...

  6. JS面向对象(2) -- this的使用,对象之间的赋值,for...in语句,delete使用,成员方法,json对象的使用,prototype的使用,原型继承与原型链

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  7. js面向对象-原型链

    var Person = function (name) { this.name = name; } Person.prototype.say = function () { console.log( ...

  8. 【JavaScript】 JS面向对象的模式与实践 (重点整治原型这个熊孩子 (/= _ =)/~┴┴ )

    参考书籍 <JavaScript高级语言程序设计>—— Nicholas C.Zakas <你不知道的JavaScript>  —— KYLE SIMPSON   在JS的面向 ...

  9. js面向对象、创建对象的工厂模式、构造函数模式、原型链模式

    JS面向对象编程(转载) 什么是面向对象编程(OOP)?用对象的思想去写代码,就是面向对象编程. 面向对象编程的特点 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有对象上继承出新的对象 ...

随机推荐

  1. CSS3和jQuery实现的自定义美化Checkbox

    效果图: 是不是比默认的好看多了,个人的审美观应该还是可以的. 当然我们可以在这里查看DEMO演示. 接下来我们一起来看看实现这款美化版Checkbox的源代码.主要思路是利用隐藏原来的checkbo ...

  2. iOS开发之CocoaPods的安装与使用

    前言部分 iOS开发时,项目中会引用许多第三方库,CocoaPods(https://github.com/CocoaPods/CocoaPods) 可以用来方便的统一管理这些第三方库. 一.安装 由 ...

  3. 让Visual Studio Code对jQuery支持智能提示!

    本人新手,对代码各种不熟悉,记不准确,总是打错,造成各种失误!! 其实这个方法应该适合大部分前端开发工具!! 园里子有前人写了一篇文章对智能提示的实现!不过很多新手看不懂吧. http://www.c ...

  4. js正则表达式中test,exec,match方法的区别

    test test 返回 Boolean,查找对应的字符串中是否存在模式.var str = "1a1b1c";var reg = new RegExp("1." ...

  5. python之消息队列

    引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松地解决这些问题.消息服务擅 ...

  6. Lind.DDD.Messaging框架通讯组件介绍

    回到目录 大 家好,今天有时间来介绍一下Lind.DDD框架里的消息机制,消息发送这块一般的实现方法是将Email,SMS等集成到一个公用类库里,而本身 Email和SMS没什么关系,它们也不会有什么 ...

  7. Lind.DDD.Manage项目核心技术分享

    回到目录 关于Lind.DDD.Manager的培训与学习 讲解:张占岭 花名:仓储大叔 主要框架:Lind.DDD,Lind.DDD.Manager 关于Lind.DDD.Manager 由于数据模 ...

  8. mysql errno 150

    mysql error Number 1005can't creat table'/test/#sql-640_1.frm'(errno:150)三种可能问题 外键和被引用外键类型不一样,比如inte ...

  9. HTML5漂亮实用的电子书

    效果体验:http://hovertree.com/texiao/html5/2.htm 点击这里下载 支持多种系统Mac,PC,Android,iPhone,iPad和Windows Phone 支 ...

  10. Objective-C 30分钟入门教程

    Objective-C 30分钟入门教程 我第一次看OC觉得这个语言的语法有些怪异,为什么充满了@符号,[]符号,函数调用没有()这个,但是面向对象的高级语言也不外乎类,接口,多态,封装,继承等概念. ...