分针网—每日分享:图例详解那道setTimeout与循环闭包的经典面试题

简单创建对象

var o  =  new  Object ( ) ;
o .name  =  'hang' ;
o .age  =  21 ;
o .sayName  =  function ( ) {
console . log ( this .name ) ;
} ;
o . sayName ( ) ;  // 'hang'
//使用字面量创建,和以上代码相等
var o = {
name : 'hang' ,
age : 21 ,
sayName : funciont ( ) {
console . log ( this .name ) ;
}
} ;
o . sayName ( ) ;  // 'hang'

  使用以上的方法创建对象很简单,但是缺点也是非常明显的,如果再次创建相同属性和方法的对象的时候,还要把代码复制一遍,会导致很多代码重复。

工厂模式

function  createObj (name ,age ) {
var o = new  Object ( ) ;
o .name =name ;
o .age =age ;
o .sayName = function ( ) {
console . log ( this .name ) ;
}
return o ;
}
var p1 = createObj ( 'xuhang' , 21 ) ;
var p2 = createObj ( 'Tomoya' , 22 ) ;
p1 . sayName ( ) ;  // 'xuhang'
p2 . sayName ( ) ;  // 'Tomoya'
console . log ( typeof p1 ) ;  // object
p1  instanceof  createObj  //false 无法判断类型

  用工厂模式来创建对象,实现了一套属性和方法可以实现多个对象可以使用,可以传递参数,但主要缺点是无法识别对象类型。

构造函数模式

function  CreateObj (name ,age ) {
this .name =name ;
this .age =age ;
this .sayName = function ( ) {
console . log ( this .name ) ;
} ;
}
var p1 = new  CreateObj ( 'xuhang' , 21 ) ;
var p2 = new  CreateObj ( 'Tomoya' , 22 ) ;
p1 . sayName ( ) ;  // xuhang
console . log (p1  instanceof  CreateObj ) ;  // true 可以判断类型

用构造函数模式,解决了工厂模式中不能判断对象类型的问题,还有必须要用new 关键字来创建对象。new关键字相当于执行了一下几步。

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

  使用构造函数创建的对象都有一个constructor属性,该属性可以标识对象类型,但一般我们还是常用instanceof来判断对象类型, 因为constructor属性仅返回构造函数类型。


p1 .constructor  === CreateObj  //true
p1 .constructor  === Object  //false

p1  instanceof  createObj  // true
p1  instanceof  Object  //true

不使用new时,函数的作用域会是全局作用域,this就会指向window对象。

  构造函数也存在问题,每个方法都要在实例上创建一遍。也就是说p1和p2的sayName()方法虽然作用相同,但这两个方法并不是同一个函数。

p1 .sayName ==p2 .sayName //false

原型模式

  原型模式解决了构造函数模式中同功能的方法的代码无法重用的问题。

  我们创建的每个函数都有一个名为prototype(原型)的属性,这个属性是一个指针,指向一个对象,这个对象被称为原型对象。原型对象有一个名叫constructor的属性,这个属性是一个指针,指向构造函数。

  使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必再构造函数中定义对象实例的信息,而是可以将这些信息添加到原型对象中。

function  CreateObj ( ) {
CreateObj .prototype .name  =  'xuhnag' ;
CreateObj .prototype .age  =  21 ;
CreateObj .prototype .sayName  =  function ( ) {
return  this .name ;
} ;
}
var p1  =  new  CreateObj ( ) ;
p1 .name ;  //'xuhang'
var p2  =  new  CreateObj ( )
p2 .name ;  //'xuhang'

  说明一下以上例子,p1的name属性,如果自身有name属性,就会返回自身的值,如果没有的话,则查找原型对象中有没有name属性,若原型对象中有name属性,则返回其值,否则,就报错。

p1 .name = '许航' ;
p1 .name ; // 许航
p2 .name ; // xuhang

  可以通过hasOwnProperty()方法检测一个属性是存在于具体的对象中,还是存在于该对象的原型对象中。

p1 . hasOwnProperty ( 'name' ) ;  //true
p2 . hasOwnProperty ( 'name' ) ;  //false

  也可以通过 in 关键字来判断一个属性是否存在于具体的对象或者该对象的原型对象中。

'name'  in p1  // true
'name'  in p2  // true

   原型模式存在的问题,它省略了为构造函数传递初始化参数,这给程序上造成了一定的不便,另外最主要的是,当我们改变值为引用类型的对象的属性时,这个改变的结果会被其他对象共享。

function  CreateObj ( ) {
}
CreateObj .prototype = {
constructor :CreateObj ,  //将原型对象重写(指向Object)
name : 'xuhang' ,
age : 21 ,
family : [ '爸爸' , '妈妈' ] ,
sayName : function ( ) {
console . log ( this .name ) ;
}
} ;
var p1 = new  CreateObj ( ) ;
var p2 = new  CreateObj ( ) ;
p1 .family . push ( '妹妹' ) ;
console . log (p2 .family ) ;  // '爸爸','妈妈','妹妹'

构造函数和原型模式结合

  构造函数模式的缺点就是无法共享方法,原型模式的方法没问题,缺点就是当原型对象属性的值引用类型时,对其进行修改会反映到所有实例中。

   将两者结合起来,对象的属性使用构造函数的模式创建,对象的方法用原型模式来创建。

function  CreateObj (name .age ) {
this .name =name ;
this .age =age ;
this .family = [ '爸爸' , '妈妈' ] ;
}
CreateObj .prototype = {
constructor :CreateObj ,
sayName : function ( ) {
console . log ( this .name ) ;
}
}
var p1 = new  CreateObj ( 'xuhang' , 21 ) ;
var p2 = new  CreateObj ( 'Tomoya' , 22 ) ;
p1 .family . push ( "妹妹" ) ;
console . log (p1 .family ) ;  // '爸爸','妈妈','妹妹'
console . log (p2 .family ) ;  // '爸爸','妈妈'

JavaScript面向对象-继承
原型链继承

  非常简单的继承关系,实例是子类的实例,也是父类的实例。父类新增的原型属性和原型方法,子类都能访问到。原理:是将父类的实例作为子类的原型。

function  Cat (name ,age ) {
this .name =name ;
this .age =age ;
this .showName = function ( ) {
console . log ( "我叫" +name + ",你们好!" ) ;
}
}
Cat .prototype .showEat = function (food ) {
console . log ( this .name + "正在吃" +food ) ;
}
var cat1 = new  Cat ( 'Ami' , 2 ) ;
function  Dog ( ) {
}
Dog .prototype = new  Cat ( ) ;  //将父类的实例作为子类的原型
var dog = new  Dog ( 'jod' , 3 ) ;  // 无法向父类传递参数;
console . log (dog . showName ( ) ) ;

  但是缺点也是很明显,要想为子类新增属性和方法,必须要在new Cat()这样的语句之后执行,不能放到构造器中。创建子类实例时,无法向父类构造函数传参。

  还有个致命的缺点,当我们改变值为引用类型的原型对象的属性时,这个改变的结果会被所有子对象共享。

构造继承

  解决了原型链继承中,子类实例共享父类引用属性的问题创建子类实例时,可以向父类传递参数可以实现多继承(call多个父类对象)。原理:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function  Cat (name ,age ) {
this .name =name ;
this .age =age ;
this .showName = function ( ) {
console . log ( "我叫" +name + ",你们好!" ) ;
}
}
Cat .prototype .showEat = function (food ) {
console . log ( this .name + "正在吃" +food ) ;
}
function  Dog (name ,age ) {
Cat . call ( this ,name ,age ) ;  // 继承属性
}
var dog1 = new  Dog ( 'job' , 3 ) ;
var dog2 = new  Dog ( 'job' , 2 ) ;
dog1 . showName ( ) ;  //我叫job,你们好!
dog1 . showEat ( '鱼' ) ;  // 报错
console . log (dog1  instanceof  Cta ) ;  // false
console . log (dog1 .showName  === dog2 .showName ) // false

  缺点有如下几点,实例并不是父类的实例,只是子类的实例。只能继承父类的实例属性和方法,不能继承原型属性/方法。无法实现函数复用,父类中的方法在每个子类中都会生成一遍,父类中的方法没有被复用。

实例继承

  不限制调用方式,返回的对象具有相同的效果。也可以继承原型属性/方法。
原理:是作为子类实例返回

function  Cat (name ,age ) {
this .name =name ;
this .age =age ;
this .showName = function ( ) {
console . log ( "我叫" +name + ",你们好!" ) ;
}
}
Cat .prototype .showEat = function (food ) {
console . log ( this .name + "正在吃" +food ) ;
}
function  Dog (name ,age ) {
var instance = new  Cat ( ) ;
instance .name =name ;
instance .age =age ;
return instance ;
}
var dog = new  Dog ( 'job' , 3 ) ;
console . log (dog  instanceof  Cat ) ;  // true;
console . log (dog  instanceof  Dog ) ;  // false;
dog . showEat ( 'fish' ) ;  // 可以使用原型方法

  缺点是,实例是父类的实例,不是子类的实例,不支持多继承。

组合继承

  组合继承就是将原型链继承和借用构造方法继承组合,发挥两者之长。原理:通过调用父类构造,继承父类的属性并保留传参,然后通过将父类实例作为子类原型。

可以继承实例属性/方法,也可以继承原型属性/方法。
既是子类的实例,也是父类的实例。
不存在引用属性共享问题。
可传参,函数可复。

function  Cat (name ,age ) {
this .name =name ;
this .age =age ;
this .showName = function ( ) {
console . log ( "我叫" +name + ",你们好!" ) ;
}
}
Cat .prototype .showEat = function (food ) {
console . log ( this .name + "正在吃" +food ) ;
}
function  Dog (name ,age ) {
// 引用父类型的属性,又调用了一次父函数
Cat . call ( this ,name ,age ) ;
}
// 继承父类型的方法,调用了一次父函数
Dog .prototype = new  Cat ( ) ;
var dog = new  Dog ( 'job' , 3 ) ;
console . log (dog  instanceof  Cat ) ;  //true;
console . log (dog  instanceof  Dog ) ;  //true;
dog . showName ( ) ;
dog . showEat ( 'fish' ) ;

  组合继承没什么太大的问题,只是调用了两次父类构造函数,仅仅消耗多了点内存。


转载: http://xhwhen.coding.me/2017/05/07/JavaScript面向对象笔记和总结/
本文转载自分针网




想学习更多IT知识 可加群:272292492

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值