js对象(一)——建立对象

首先是对“对象”的理解:

对象是一组没有特定属性的值,对象的每个属性或方法都有一个名字,而每个名字都映射到一个值,其中值能够是数据或函数。每个对象都是基于一个引用类型建立的,这个引用类型能够是原生类型,也能够是开发人员自定义的类型。——高程

(好的,这里说的比较不容易理解)javascript

(不急,接下来再看)java

JavaScript中,一切都是对象,函数也是对象,数组也是对象,可是数组是对象的子集,而对于函数来讲,函数与对象之间有一种“鸡生蛋蛋生鸡”的关系。

全部的对象都是由Object继承而来,而Object对象倒是一个函数。对象都是由函数来建立的。数组

好比,在控制台中
输入 typeof Object 结果是"function",
输入 typeof Function 结果仍是"function".
图片描述浏览器

(好的,是否是更懵逼了,不急,如今先看一下怎么建立对象以及各类方法的孰优孰劣)安全

建立对象

0. 最基本的模式:

var box=new Object(); //建立一个 Object 对象
box.name='Lee'; //建立一个 name 属性并赋值
box.age= 100; //建立一个 age 属性并赋值
box.run= function(){ //建立一个run()方法并返回值 
 return this.name + this.age; 
}; 
console.log(box.run()); //输出属性和方法的值

优缺点:app

  • 优势:简单
  • 缺点:产生大量代码,封装性差

1.工厂模式:

fuction creatPerson(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 = creatPerson("Nicholas",29,"engineer");//实例化
var person2 = creatPerson("Mike",28,"teacher");

优缺点:函数

  • 优势:解决了建立多个类似对象的问题.
  • 缺点:但却没有解决对象识别问题,即怎样知道一个对象的类型。也就是,由于根本没法搞清楚他们究竟是哪一个对象的实例(这个能够和下面的构造函数模式做对比)

2.构造函数模式

function Person(name,age,job){  
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
      console.log(this.name);
   }
}

//或
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;//注意这里不要写括号,要实现引用地址一致
}
function sayName(){
    console.log(this.name);
    
}//这里是在外面写一个,但这种方法会有做用域问题,十分不推荐
 var person1 = new Person("Nicholas",29,"Engineer");//通常这样实例化
 
 var o = new Object;;
 Person.call(o,"Kkresten",25,"Nurse");//对象冒充法实例化

区别:学习

  • 没有写出new Object,可是后台会自动 var obj = new Object,而this就至关于obj
  • 没有 renturn 语句,在后台返回

规范this

  • 函数名和实例化构造名相同且大写(非强制,主要是为了和普通函数区分开来)
  • 经过构造函数建立对象,必须使用 new 运算符

优缺点spa

  • 优势: 建立自定义的构造类型意味着未来能够将它的实例标识为一种特定的类型,(能够用instanceof 来验证),便可识别(这里就能够和上面工厂模式做对比了,这也是比工厂模式更强的地方)
  • 缺点:每一个方法都要在每一个实例上建立一遍,大可没必要(当函数在内部时),

全局做用域中定义的函数只能被某个对象调用,这让全局做用域有点名存实亡,并且,若是对象须要定义不少方法,那么就要定义不少个全局函数,因而,这个自定义的引用类型就丝毫没有封可言了(函数定义在外部时)。(因此函数在内部和外部都有缺点)

(好的,学到这里,你已经大致掌握了怎么建立一个对象,接下来将开始学习建立对象高大上的方法和概念)

3. 原型模式

咱们建立的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,(属性值是对象)而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。:prototype 经过 调用构造函数而建立的那个对象的原型对象。使用原型的好处可让全部对象实例共享它所包含的属性和方法。也就是说,没必要在构造函数中定义对象信息,而是能够直接将这些信息 添加到原型中。———高程

(好的,又回到了懵逼的状态了,不急,先经过例子和图来了解一下先)

function Person(){//构造函数
}

Person.prototype.name = "Nicholas";//添加原型属性
Person.prototype.age = 29;
Person.prototype.job = "software Engineer";
Person.prototype.sayName = function() {//添加原型方法
   console.log(this.name);
}

var person1 = new Person(); //实例化
person1.sayName(); //“Nicholas”
var person2 = new Person(); //实例化
person2.sayName();//"Nicholas"

以下图:
图片描述
图片描述

而我本身画了个图来加深一下认识(结合高程里的那段话)
图片描述
(是否是有点懂了,接下来再逐个仔细分析)

1.对于[[Prototype]]

  • 每个对象都有一个这样的隐藏属性,它引用了建立这个对象的函数的prototype原型对象,咱们来看一张图:

图片描述
注意:函数也是对象,天然它也有__proto__。
在控制台中,咱们发现:
图片描述

即函数的__proto__是函数类型。(也就说函数的原型对象是函数,而函数也是对象,因此函数的原型仍是对象)(这里听着有点绕,可是能够先跳过)

还要注意一个特例,以下图:
图片描述

这里,一切对象继承自Object,而咱们又知道Object.prototype是它的原型对象,是一个对象,可是这个对象的__proto__却为null,是否说明构建Object对象的函数没有原型对象,由于对象都是由函数建立的

(对于函数与对象的关系和涉及到的原型链的相关知识,还挺大挺深的,将单独做为一个话题来讨论。若是这里有点看得晕,能够先只是知道prototype是什么就能够了)

注意: __proto__这个指针没有标准的方法访问,IE 浏览器在脚本访问[[Prototype]]会不能识别,火狐和谷歌浏览器及其余某些浏览器均能识别。虽然能够输出,但没法获取内部信息。([[Prototype]] 也可写为__proto__)虽然没法访问到,可是能够经过: Object.isPrototypeOf(person1)判断这个实例对象是否指向它的原型对象 ;而咱们也知道Person.prototype就是Object类型,即一个原型对象

//承接上面的代码
Person.prototype.isPrototypeOf(person1);//true
Person.prototype.isPrototypeOf(person2);//true

2.对于原型模式的执行流程:

①先检查这个对象自身有无这个属性;若是有,直接使用它。

②若是没法在对象自身找到须要的属性,就会继续访问对象的[[Prototype]]链,找到则直接使用,再也不查找下去;若是一直找不到,最后就会返回undefined

3.能够经过 hasOwnProperty()方法检测属性是否存在实例中,也能够经过 in 来判断 实例或原型中是否存在属性;能够经过Object.keys()方法或Object.getOwnPropertyNames()来获得实例属性,具体见高程。

4.优缺点:每添加一个属性和方法就要敲一遍Person.prototype,并且视觉上说封装性不够好。固然优势就是解决了上面构造函数的问题。

5.更简单的原型模式

function Person(){
}
 Person.prototype = {  //将 Person.prototype 设置为等于一个以对象字面量形式建立的新对象
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
//(但constructor属性再也不指向Person了,而是指向Object构造函数)
//但能够这样手动设置:
 function Person(){
}
 Person.prototype = {  
    constructor : Person,//手动设置
    name : "Nicholas",
    age: 29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
//由于按上面的方式会致使它的[[Enumerable]]特性被设置为true,因此还能够像下面这样
 function Person(){
}
 Person.prototype = {  
    name : "Nicholas",
    age: 29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
Object.definePrototype(Person.prototype,"constructor"),{
   enumerable :  false;
   value : Person
  }
}

6.原型的动态性:

//承接上面的Person构造函数
var friend = new Person();
Person.prototype.sayhi = function(){
    alert("hi");
};
friend.sayhi(); //"hi"没有问题,虽然是在实例以后添加的属性,可是根据原型模式的搜索机制,会找到原型中的这个方法,缘由:实例与原型是松散链接的
//可是:若是是这样:
 function Person(){
}
 var friend = new Person();
 Person.prototype = {  
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
     sayName : function() {
       console.log(this.name);
    }
 }
 friend.sayName();//Uncaught TypeError: friend.sayName is not a function,虽然有将重写的原型的指针指向Person原型对象,可是很实际上却以下图:

图片描述

6.优缺点:

  • 优势:解决了构造函数出现的问题(强大的类型识别)
  • 缺点:共享了引用类型的值,这个就是不多有人单独使用原型模式的缘由。好比下面:
function Person(){
}
 Person.prototype = {  
    constructor : Person,
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
    friend:["Mike","Jeny"],
    sayName : function() {
       console.log(this.name);
     }
}
var person1 = new Person();
var person2 = new Person();
person1.friend.push("Van");
console.log(person1.friend);//"Mike,Jeny,Van"
console.log(person2.friend);//"Mike,Jeny,Van"

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

function Perosn(name,age,job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.friends = ["Shelby","Court'];
}
Person.prototype = {
 constructor : Person,
 sayName : function(){
    console.log(this.name);
  }
}
var person1 = new Person("Nicholas",29," Engineer");

优缺点:

  • 优势:解决了引用类型实例共享的问题
  • 缺点:封装性不够好

## 5.动态原型模式 ##

function Person(name,age,job){
//属性
    this.name = name; 
    this.age = age;
    this.job = job;
//方法
if(typeof this.sayname != "function"){
  Person.prototype.sayname = function(){
   console.log(this.name);//只有在sayName方法不存在的状况下才会被添加到原型中
  }
}
//这段代码在初次调用构造函数时才会执行,此后,原型已经初始化
var friend  = new Person("Nicholas",29,"Engineer");

优缺点:

  • 优势:既获得了封装,又实现了原型方法共享,而且属性都保持独立。能够说是很是完美了,其实说白了这种方法就是解决上面构造函数的方法不须要每次都建立一遍的问题。
  • 缺点:不能使用对象字面量重写原型,会使以前定义的原型对象的方法失效。

(好了,学到这里,大概经常使用的建立对象的方法就已经掌握了,接下来还有两种不经常使用的方法能够了解一下)

6.寄生构造函数模式

function Person(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 friend = new Person("Nicholas",29,"Software Engineer");

function SpecialArray(){
//建立数组
var values = new Array();
//用push方法初始化数组的值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
 return this.join("|");
}
//返回数组
 return values;
}
var colors = new SpecialArray("red","blue","green");
console.log(colors.toPipedString()); //"red|blue|green"

优缺点:

  • 构造函数返回的对象与在构造函数外部建立的对象没有什么不一样,为此不能依赖instanceof操做符来肯定对象的类型:
console.log(friend instanceof Person) // false

所以,可使用其余模式的状况下不使用此类型

7.稳妥构造函数模式

function Person(name,age,job){
 //建立要返回的对象
 var o = new Object();
//能够在这里定义私有变量和函数
//添加方法
 o.sayName = function(){
 console.log(name);
 }
//返回对象
  return o;
}
var friend =  Person("Nicholas",29,"Software Engineer");
friend.sayName();

区别:

  • 不引用this的对象
  • 不使用new操做符

优势:安全

(好了,js对象的建立就大概有这几种方法,其实最经常使用的貌似仍是构造函数的模式,可是原型相关的东西也是必需要掌握的)

最后,欢迎你们围观指正!

参考:《javascript高级程序设计》

相关文章
相关标签/搜索