js对象——创建对象(1)

2018-12-14  本文已影响0人  方_糖

创建多个相似对象时使用的方法:

1. 工厂模式
//工厂模式
function createPerson(name,age,job){
  var o=new Object();
  o.name=name;
  o.age=age;
  o.job=job;
  o.sayName=function(){
    alert(this.name);
  };
  return o;   //将创建的对象返回
}
var person=createPerson("Jim",22,"Doctor");

2.构造函数模式
//构造函数模式
function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.sayName=function(){
    alert(this.name);
  }
}

var person=new Person("Jim",22,"Doctor");
function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.sayName=sayName;
}
function sayName(){
  alert(this.name);
}

这样做确实解决了两个函数做同一件事的问题,可是新问题是:(a)全局函数不再“全局”了,而只能由Person对象调用(b)没有了“封装性”。如果对象需要定义很多方法,那么就要定义很多个的全局函数,于是我们的自定义的引用类型就丝毫没有封装性可言了。而接下来的原型模式就可以解决这个问题

3.原型模式
//原型模式
function Person(){}
Person.prototype.name="Jim";
Person.prototype.age=22;
Person.protoType.job="Doctor";
Person.protoType.sayName=function(){
  alert(this.name);
}
var person=new Person();
person.sayName();   //Jim
function Person(){}
Person.prototype.name="Jim";
Person.prototype.age=22;
Person.protoType.job="Doctor";
Person.protoType.sayName=function(){
  alert(this.name);
}
var person=new Person();
person.name="Tom";
alert(person.name);   //Tom

此时person的name被一个新值给屏蔽了
(4)使用delete操作符删除 实例 属性

person.name="Tom";
alert(person.name);   //Tom
delete person.name;
alert(person.name);   //Jim

(5)hasOwnProperty()方法检测一个属性时存在于实例中还是原型中

var person=new Person();
alert(person.hasOwnProperty("name"))  //false:存在原型中
person.name="Tom";
alert(person.hasOwnProperty("name"))  //true:存在对象实例中

(6)判断对象中传入的参数类型(只要有prototype属性的对象都能用)Object.prototype.toString.call(options)
使用方法:https://www.cnblogs.com/wyaocn/p/5796142.html
例如判断传入的参数是否为对象

function Person(name) {
    alert(Object.prototype.toString.call(name))
}
var Tim={
    age:11,
    job:"student",
}
var person=new Person(Tim);  //[object,object]
var person=new Person;
alert( "name" in person );    //true
person.name="Tom";    //将name写在实例中
alert( "name" in person );    //true

所以想要确定该属性是存在于原型中还是实例中,只需要同时使用hasOwnPropertyin操作符

function hasPrototypeProperty(object,name){
  return !object.hasOwnProperty(name)&&(name in object);  //true:在原型中,false:在实例中
}

for-in循环,所有开发人员定义的属性都是可枚举的(IE8-除外),所以所有属性都可以(实例属性和原型属性)使用for-in循环

for(var prop in person){
  if(prop=="name"){
    alert("Found name!")  ;  //IE除外
  }
}

IE8-的解决办法见《JavaScript高级程序设计》(第三版)——人民邮电出版社,P153~154

function Person(){}
Person.prototype={
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
}

注意,此时的constructor属性不再指向Person了。前面介绍过,每创建一个函数,会同时创建它的prototype对象,这个对象会自动获取constructor属性,这里本质上重写了默认的prototype对象,此时constructor属性指向Object构造函数,经管如此,instanceof操作符还能返回正确的结果。
如果此时constructor的值很重要,可以在person.prototype中设置它,但会导致constructor的默认不可枚举特性被设置为可枚举。

function Person(){}
Person.prototype={
  constructor:Person,
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
}

如果要兼容ES5,可以试一下object.definProperty()

function Person(){}
Person.prototype={
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
};
//重设构造函数,只适用于ES5兼容的浏览器
Object.definPrototype(Person.prototype,"constructor",{
  enumerable:false, //不可枚举
  value:Person,
})
function Person(){}
Person.prototype={
  friends:["Amy","Sam"],
};
var person1=new Person();
var person2=new Person();
person1.friends.push("Van");
alert(person1.friends);     //"Amy,Sam,Van"
alert(person2.friends);     //"Amy,Sam,Van"

当没有对Person的friends赋值时,直接调用原型属性并修改,就会直接导致原型修改。
而这个问题证实我们很少有人单独使用原型模式的问题所在。解决办法看下面。

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

创建自定义类型最常见的方式,就是组合构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性

//组合使用构造模式和原型模式
function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.friends=["Amy","Tom"];
}
Person.prototype={
  constructor:Person,
  sayName:function(){
    alert(this.name);
  }
}
var person1=new Person();
var person2=new Person();

person1.friends.push("Van");
alert(person1.friends);   //Amy,Tom,Van
alert(person2.friends);   //Amy,Tom

总结:

在没有类的情况下,可以使用下列模式创建对象:
(1)工厂模式:在函数里面创建对象,并添加属性和方法。但没有解决对象识别问题,最后被构造函数模式所取代。
(2)构造函数模式:可以像创建内置对象实例一样使用new操作符。但没函数不局限于任何对象,因此没有了封装性。
(3)原型模式:使用构造函数的prototype属性指定应该共享的属性和方法。但单独使用原型模式的话,就会使不需要共享的属性和方法被共享
(4)组合使用构造函数模式和原型模式:使用构造函数定义实例属性,使用原型定义共享的属性和方法

---------------------------《JavaScript高级程序设计(第三版)》读书笔记-------------------------------------------

扩展:

5. JSON格式的JS对象

var Circle={
   "PI":3.14159,
   "area":function(r){
     return this.PI * r * r;
  }
};
alert( Circle.area(1.0) );

5. ES6的class写法

//类的基本写法
class Point{
    //属性
    constructor(x,y){
        this.x=x;
        this.y=y;
    }
    //方法
    toString(){
        console.log(this.x+"-"+this.y)
    }
    //只能通过类(Point)调用的方法,不会被实例(point)继承 
    //静态方法:加上static关键字
    static classMethod(){
        console.log("I can only be used by Point")
    }
}

---------------------------《JavaScript高级程序设计(第三版)》读书笔记-------------------------------

附:相关文章

js对象——继承(2)

上一篇 下一篇

猜你喜欢

热点阅读