金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 对象的创建,创建对象

对象的创建,创建对象

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-10-09 20:27

前端基础进阶(9):详解面向对象、构造函数、原型与原型链

2017/04/02 · JavaScript · 1 评论 · 原型, 原型链, 构造函数, 面向对象

原文出处: 波同学   

金沙棋牌官方平台 1

.

如果要我总结一下学习前端以来我遇到了哪些瓶颈,那么面向对象一定是第一个毫不犹豫想到的。尽管我现在对于面向对象有了一些的了解,但是当初的那种似懂非懂的痛苦,依然历历在目。

为了帮助大家能够更加直观的学习和了解面向对象,我会用尽量简单易懂的描述来展示面向对象的相关知识。并且也准备了一些实用的例子帮助大家更加快速的掌握面向对象的真谛。

  • jQuery的面向对象实现
  • 封装拖拽
  • 简易版运动框架封装

这可能会花一点时间,但是却值得期待。所以如果有兴趣的朋友可以来简书和公众号关注我。

而这篇文章主要来聊一聊关于面向对象的一些重要的基本功。

Object类型是JavaScript中使用最多的一种类型。创建Object实例的方式有多种,接下来一一列举。

  1. 面向对象具有类的概念,通过类可以创建任意多个相同的属性和方法。

  2. ECMA-262把对象定义为:无序属性的集合,其属性包含基本值、对象或函数。

  3. ECMAScript中有两种属性:数据属性,访问器属性。

今天从家里回到了学校,在家呆了十天,胖了几斤的重量,又折腾回学校了,春节回家真是艰辛的路途。随便扯扯我的往返行程:为了省钱我没有选择直飞到长春往返都是到北京转的,这样我和女朋友可以节省4000块左右。1月24号深圳-飞机-北京(飞机晚点1个小时),到北京已经凌晨两点多,机场大巴就剩两个了,做一个大巴到了市里在打的到同学那100块没了,到同学那(天通苑附近)都三点了,吃饭喝酒(一瓶牛栏山+2瓶啤酒)到了五点,睡觉到十点,起床去打了两个小时的篮球,回来没吃饭地铁去车站,差一点没有赶上动车去长春,晚上九点多到了长春,打车去长春理工同学那住,晚上要了肯德基外卖,100块又没了,十二点睡觉三点起床赶车回松原下乡参加同学婚礼,下午1点回来做汽车回家。哎,折腾死人了,放假在家天天叔叔大爷家轮流吃饭,天天打麻将......2月6号四点半起床赶车去市里做火车去长春为了赶上10点25分的车去北京,九点左右到了北京,坐机场块钱去首都机场,坐飞机直飞香港,到香港凌晨3点多,出战坐大巴去深圳湾回深圳,到宿舍8点多。感觉一直都是在赶车,好累,有钱就直飞,哼。

一、对象的定义

在ECMAScript-262中,对象被定义为“无序属性的集合,其属性可以包含基本值,对象或者函数”

也就是说,在JavaScript中,对象无非就是由一些列无序的key-value对组成。其中value可以是基本值,对象或者函数。

// 这里的person就是一个对象 var person = { name: 'Tom', age: 18, getName: function() {}, parent: {} }

1
2
3
4
5
6
7
// 这里的person就是一个对象
var person = {
    name: 'Tom',
    age: 18,
    getName: function() {},
    parent: {}
}

创建对象

我们可以通过new的方式创建一个对象。

var obj = new Object();

1
var obj = new Object();

也可以通过对象字面量的形式创建一个简单的对象。

var obj = {};

1
var obj = {};

当我们想要给我们创建的简单对象添加方法时,可以这样表示。

// 可以这样 var person = {}; person.name = "TOM"; person.getName = function() { return this.name; } // 也可以这样 var person = { name: "TOM", getName: function() { return this.name; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 可以这样
var person = {};
person.name = "TOM";
person.getName = function() {
    return this.name;
}
 
// 也可以这样
var person = {
    name: "TOM",
    getName: function() {
        return this.name;
    }
}

访问对象的属性和方法

假如我们有一个简单的对象如下:

var person = { name: 'TOM', age: '20', getName: function() { return this.name } }

1
2
3
4
5
6
7
var person = {
    name: 'TOM',
    age: '20',
    getName: function() {
        return this.name
    }
}

当我们想要访问他的name属性时,可以用如下两种方式访问。

person.name // 或者 person['name']

1
2
3
4
person.name
 
// 或者
person['name']

如果我们想要访问的属性名是一个变量时,常常会使用第二种方式。例如我们要同时访问person的name与age,可以这样写:

['name', 'age'].forEach(function(item) { console.log(person[item]); })

1
2
3
['name', 'age'].forEach(function(item) {
    console.log(person[item]);
})

这种方式一定要重视,记住它以后在我们处理复杂数据的时候会有很大的帮助。

1. Object构造函数

var person = new Object();
person.name = "Brittany";
person.age = 23;
person.job = "web front-end engineer";
person.sayName = function() {
    console.log(this.name);
};
person.sayName();   //Brittany

4. 数据属性: configurable, enumerable, writable, value

 

二、工厂模式

使用上面的方式创建对象很简单,但是在很多时候并不能满足我们的需求。就以person对象为例。假如我们在实际开发中,不仅仅需要一个名字叫做TOM的person对象,同时还需要另外一个名为Jake的person对象,虽然他们有很多相似之处,但是我们不得不重复写两次。

var perTom = { name: 'TOM', age: 20, getName: function() { return this.name } }; var perJake = { name: 'Jake', age: 22, getName: function() { return this.name } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var perTom = {
    name: 'TOM',
    age: 20,
    getName: function() {
        return this.name
    }
};
 
var perJake = {
    name: 'Jake',
    age: 22,
    getName: function() {
        return this.name
    }
}

很显然这并不是合理的方式,当相似对象太多时,大家都会崩溃掉。

我们可以使用工厂模式的方式解决这个问题。顾名思义,工厂模式就是我们提供一个模子,然后通过这个模子复制出我们需要的对象。我们需要多少个,就复制多少个。

var createPerson = function(name, age) { // 声明一个中间对象,该对象就是工厂模式的模子 var o = new Object(); // 依次添加我们需要的属性与方法 o.name = name; o.age = age; o.getName = function() { return this.name; } return o; } // 创建两个实例 var perTom = createPerson('TOM', 20); var PerJake = createPerson('Jake', 22);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var createPerson = function(name, age) {
 
    // 声明一个中间对象,该对象就是工厂模式的模子
    var o = new Object();
 
    // 依次添加我们需要的属性与方法
    o.name = name;
    o.age = age;
    o.getName = function() {
        return this.name;
    }
 
    return o;
}
 
// 创建两个实例
var perTom = createPerson('TOM', 20);
var PerJake = createPerson('Jake', 22);

相信上面的代码并不难理解,也不用把工厂模式看得太过高大上。很显然,工厂模式帮助我们解决了重复代码上的麻烦,让我们可以写很少的代码,就能够创建很多个person对象。但是这里还有两个麻烦,需要我们注意。

第一个麻烦就是这样处理,我们没有办法识别对象实例的类型。使用instanceof可以识别对象的类型,如下例子:

var obj = {}; var foo = function() {} console.log(obj instanceof Object); // true console.log(foo instanceof Function); // true

1
2
3
4
5
var obj = {};
var foo = function() {}
 
console.log(obj instanceof Object);  // true
console.log(foo instanceof Function); // true

因此在工厂模式的基础上,我们需要使用构造函数的方式来解决这个麻烦。

2. 对象字面量模式

var person = {
    name: "Brittany",
    age: 23,
    job: "web front-end engineer",
    sayName: function() {
        console.log(this.name);
    }
};
person.sayName();

虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,可以使用工厂模式的一种变体。

  1. 修改属性默认的特性: Object.defineProperty();

就扯到这,今天和大伙一起学习javascript中的对象和创建对象的一些方式。

三、构造函数

在JavaScript中,new关键字可以让一个函数变得与众不同。通过下面的例子,我们来一探new关键字的神奇之处。

function demo() { console.log(this); } demo(); // window new demo(); // demo

1
2
3
4
5
6
function demo() {
    console.log(this);
}
 
demo();  // window
new demo();  // demo

为了能够直观的感受他们不同,建议大家动手实践观察一下。很显然,使用new之后,函数内部发生了一些变化,让this指向改变。那么new关键字到底做了什么事情呢。嗯,其实我之前在文章里用文字大概表达了一下new到底干了什么,但是一些同学好奇心很足,总期望用代码实现一下,我就大概以我的理解来表达一下吧。

// 先一本正经的创建一个构造函数,其实该函数与普通函数并无区别 var Person = function(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } // 将构造函数以参数形式传入 function New(func) { // 声明一个中间对象,该对象为最终返回的实例 var res = {}; if (func.prototype !== null) { // 将实例的原型指向构造函数的原型 res.__proto__ = func.prototype; } // ret为构造函数执行的结果,这里通过apply,将构造函数内部的this指向修改为指向res,即为实例对象 var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); // 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象 if ((typeof ret === "object" || typeof ret === "function") && ret !== null) { return ret; } // 如果没有明确指定返回对象,则默认返回res,这个res就是实例对象 return res; } // 通过new声明创建实例,这里的p1,实际接收的正是new中返回的res var p1 = New(Person, 'tom', 20); console.log(p1.getName()); // 当然,这里也可以判断出实例的类型了 console.log(p1 instanceof Person); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 先一本正经的创建一个构造函数,其实该函数与普通函数并无区别
var Person = function(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        return this.name;
    }
}
 
// 将构造函数以参数形式传入
function New(func) {
 
    // 声明一个中间对象,该对象为最终返回的实例
    var res = {};
    if (func.prototype !== null) {
 
        // 将实例的原型指向构造函数的原型
        res.__proto__ = func.prototype;
    }
 
    // ret为构造函数执行的结果,这里通过apply,将构造函数内部的this指向修改为指向res,即为实例对象
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
 
    // 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
 
    // 如果没有明确指定返回对象,则默认返回res,这个res就是实例对象
    return res;
}
 
// 通过new声明创建实例,这里的p1,实际接收的正是new中返回的res
var p1 = New(Person, 'tom', 20);
console.log(p1.getName());
 
// 当然,这里也可以判断出实例的类型了
console.log(p1 instanceof Person); // true

JavaScript内部再通过其他的一些特殊处理,将var p1 = New(Person, 'tom', 20); 等效于var p1 = new Person('tom', 20);。就是我们认识的new关键字了。具体怎么处理的,我也不知道,别刨根问底了,一直回答下去太难 – -!

老实讲,你可能很难在其他地方看到有如此明确的告诉你new关键字到底对构造函数干了什么的文章了。理解了这段代码,你对JavaScript的理解又比别人深刻了一分,所以,一本正经厚颜无耻求个赞可好?

当然,很多朋友由于对于前面几篇文章的知识理解不够到位,会对new的实现表示非常困惑。但是老实讲,如果你读了我的前面几篇文章,一定会对这里new的实现有似曾相识的感觉。而且我这里已经尽力做了详细的注解,剩下的只能靠你自己了。

但是只要你花点时间,理解了他的原理,那么困扰了无数人的构造函数中this到底指向谁就变得非常简单了。

所以,为了能够判断实例与对象的关系,我们就使用构造函数来搞定。

var Person = function(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } var p1 = new Person('Ness', 20); console.log(p1.getName()); // Ness console.log(p1 instanceof Person); // true

1
2
3
4
5
6
7
8
9
10
11
12
var Person = function(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        return this.name;
    }
}
 
var p1 = new Person('Ness', 20);
console.log(p1.getName());  // Ness
 
console.log(p1 instanceof Person); // true

关于构造函数,如果你暂时不能够理解new的具体实现,就先记住下面这几个结论吧。

  • 与普通函数相比,构造函数并没有任何特别的地方,首字母大写只是我们约定的小规定,用于区分普通函数;
  • new关键字让构造函数具有了与普通函数不同的许多特点,而new的过程中,执行了如下过程:
    1. 声明一个中间对象;
    2. 将该中间对象的原型指向构造函数的原型;
    3. 将构造函数的this,指向该中间对象;
    4. 返回该中间对象,即返回实例对象。

3. 工厂模式 

function createPerson(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 = createPerson("Brittany", 23, "Software Engineer");
var person2 = createPerson("Sam", 26, "Software Engineer");
console.log(typeof person1);   //Object

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。如代码中只能检测出person1为Object类型。随着JavaScript的发展,又一个新模式出现了。

6. Object.defineProperty(person, name, {

 

四、原型

虽然构造函数解决了判断实例类型的问题,但是,说到底,还是一个对象的复制过程。跟工厂模式颇有相似之处。也就是说,当我们声明了100个person对象,那么就有100个getName方法被重新生成。

这里的每一个getName方法实现的功能其实是一模一样的,但是由于分别属于不同的实例,就不得不一直不停的为getName分配空间。这就是工厂模式存在的第二个麻烦。

显然这是不合理的。我们期望的是,既然都是实现同一个功能,那么能不能就让每一个实例对象都访问同一个方法?

当然能,这就是原型对象要帮我们解决的问题了。

我们创建的每一个函数,都可以有一个prototype属性,该属性指向一个对象。这个对象,就是我们这里说的原型。

当我们在创建对象时,可以根据自己的需求,选择性的将一些属性和方法通过prototype属性,挂载在原型对象上。而每一个new出来的实例,都有一个__proto__属性,该属性指向构造函数的原型对象,通过这个属性,让实例对象也能够访问原型对象上的方法。因此,当所有的实例都能够通过__proto__访问到原型对象时,原型对象的方法与属性就变成了共有方法与属性。

我们通过一个简单的例子与图示,来了解构造函数,实例与原型三者之间的关系。

由于每个函数都可以是构造函数,每个对象都可以是原型对象,因此如果在理解原型之初就想的太多太复杂的话,反而会阻碍你的理解,这里我们要学会先简化它们。就单纯的剖析这三者的关系。

// 声明构造函数 function Person(name, age) { this.name = name; this.age = age; } // 通过prototye属性,将方法挂载到原型对象上 Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); var p2 = new Person('jak', 22); console.log(p1.getName === p2.getName); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
// 通过prototye属性,将方法挂载到原型对象上
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
var p2 = new Person('jak', 22);
console.log(p1.getName === p2.getName); // true

金沙棋牌官方平台 2

图示

通过图示我们可以看出,构造函数的prototype与所有实例对象的__proto__都指向原型对象。而原型对象的constructor指向构造函数。

除此之外,还可以从图中看出,实例对象实际上对前面我们所说的中间对象的复制,而中间对象中的属性与方法都在构造函数中添加。于是根据构造函数与原型的特性,我们就可以将在构造函数中,通过this声明的属性与方法称为私有变量与方法,它们被当前被某一个实例对象所独有。而通过原型声明的属性与方法,我们可以称之为共有属性与方法,它们可以被所有的实例对象访问。

当我们访问实例对象中的属性或者方法时,会优先访问实例对象自身的属性和方法。

function Person(name, age) { this.name = name; this.age = age; this.getName = function() { console.log('this is constructor.'); } } Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); p1.getName(); // this is constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        console.log('this is constructor.');
    }
}
 
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
 
p1.getName(); // this is constructor.

在这个例子中,我们同时在原型与构造函数中都声明了一个getName函数,运行代码的结果表示原型中的访问并没有被访问。

我们还可以通过in来判断,一个对象是否拥有某一个属性/方法,无论是该属性/方法存在与实例对象还是原型对象。

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); console.log('name' in p1); // true

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
 
console.log('name' in p1); // true

in的这种特性最常用的场景之一,就是判断当前页面是否在移动端打开。

isMobile = 'ontouchstart' in document; // 很多人喜欢用浏览器UA的方式来判断,但并不是很好的方式

1
2
3
isMobile = 'ontouchstart' in document;
 
// 很多人喜欢用浏览器UA的方式来判断,但并不是很好的方式

更简单的原型写法

根据前面例子的写法,如果我们要在原型上添加更多的方法,可以这样写:

function Person() {} Person.prototype.getName = function() {} Person.prototype.getAge = function() {} Person.prototype.sayHello = function() {} ... ...

1
2
3
4
5
6
function Person() {}
 
Person.prototype.getName = function() {}
Person.prototype.getAge = function() {}
Person.prototype.sayHello = function() {}
... ...

除此之外,我还可以使用更为简单的写法。

function Person() {} Person.prototype = { constructor: Person, getName: function() {}, getAge: function() {}, sayHello: function() {} }

1
2
3
4
5
6
7
8
function Person() {}
 
Person.prototype = {
    constructor: Person,
    getName: function() {},
    getAge: function() {},
    sayHello: function() {}
}

这种字面量的写法看上去简单很多,但是有一个需要特别注意的地方。Person.prototype = {}实际上是重新创建了一个{}对象并赋值给Person.prototype,这里的{}并不是最初的那个原型对象。因此它里面并不包含constructor属性。为了保证正确性,我们必须在新创建的{}对象中显示的设置constructor的指向。即上面的constructor: Person

4. 构造函数模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        console.log(this.name);
    }    
}
var person1 = new Person("Brittany", 23, "Web front-end engineer");
var person2 = new Person("Closure", 26, "Manager");
person1.sayName();
person2.sayName();
console.log(person1.sayName == person2.sayName);   //false

使用构造函数的主要问题:每个方法都要在每个实例上重新创建一遍。如代码中所示,person1的sayName和person2的sayName不相等。可以将函数定义转移到构造函数外部来解决。

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

sayName函数的定义转移到了构造函数外部。而在构造函数内部,我们将sayName属性设置成等于全局的sayName函数。这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这的确解决了两个函数做同一件事的问题,可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让让人无法接受的是:如果需要定义很多方法,那就要定义很多个全局函数,于是这个自定义的引用类型就无封装性可言。这些问题可通过使用原型模式来解决。

  writable: false,

对象

四、原型链

金沙棋牌官方平台,原型对象其实也是普通的对象。几乎所有的对象都可能是原型对象,也可能是实例对象,而且还可以同时是原型对象与实例对象。这样的一个对象,正是构成原型链的一个节点。因此理解了原型,那么原型链并不是一个多么复杂的概念。

我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?

先随意声明一个函数:

function foo() {}

1
function foo() {}

那么我们可以用如下的图来表示这个函数的原型链。

金沙棋牌官方平台 3

原型链

其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。

基于原型链的特性,我们可以很轻松的实现继承

5. 原型模式

  value: 'xhk'
  })

 

五、继承

我们常常结合构造函数与原型来创建一个对象。因为构造函数与原型的不同特性,分别解决了我们不同的困扰。因此当我们想要实现继承时,就必须得根据构造函数与原型的不同而采取不同的策略。

我们声明一个Person对象,该对象将作为父级,而子级cPerson将要继承Person的所有属性与方法。

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function() { return this.name; }

1
2
3
4
5
6
7
8
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.getName = function() {
    return this.name;
}

首先我们来看构造函数的继承。在上面我们已经理解了构造函数的本质,它其实是在new内部实现的一个复制过程。而我们在继承时想要的,就是想父级构造函数中的操作在子级的构造函数中重现一遍即可。我们可以通过call方法来达到目的。

// 构造函数的继承 function cPerson(name, age, job) { Person.call(this, name, age); this.job = job; }

1
2
3
4
5
// 构造函数的继承
function cPerson(name, age, job) {
    Person.call(this, name, age);
    this.job = job;
}

而原型的继承,则只需要将子级的原型对象设置为父级的一个实例,加入到原型链中即可。

// 继承原型 cPerson.prototype = new Person(name, age); // 添加更多方法 cPerson.prototype.getLive = function() {}

1
2
3
4
5
// 继承原型
cPerson.prototype = new Person(name, age);
 
// 添加更多方法
cPerson.prototype.getLive = function() {}

金沙棋牌官方平台 4

原型链

当然关于继承还有更好的方式,这里就不做深入介绍了,以后有机会再详细解读吧。

1)对象创建 

function Person() {}
Person.prototype.name = "Brittany";
Person.prototype.age = 23;
Person.prototype.job = "Web front-end engineer";
Person.prototype.getName = function() {
    console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();

console.log(p1.name);                    //Brittany
console.log(p2.name);                    //Brittany
console.log(p1.getName == p2.getName);   //true

实例中创建的属性会覆盖原型中的同名属性,不能修改原型中的属性。

p1.name = "Susan";
console.log(p1.name);                    //Susan

hasOwnProperty()检测一个属性是否存在于实例中。

console.log(p2.hasOwnProperty("name"));  //false
p2.name = "koko";
console.log(p2.hasOwnProperty("name"));  //true
delete p2.name;
console.log(p2.hasOwnProperty("name"));  //false

isPropertyOf()

console.log(Person.prototype.isPrototypeOf(p1));   //true
console.log(Person.prototype.isPrototypeOf(p2));   //true

getPropertyOf()

console.log(Object.getPrototypeOf(p1) == Person.prototype);  //true
console.log(Object.getPrototypeOf(p1));                      //Person

 person.name  // 'xhk'

javascript中什么是对象呢,javascript中一切都是对象,任何对象的都是基于Object的,确切的说每一个对象都是基于一个引用类型创建的,这个引用类型可以是原生的引用类型也可以是我们自己创建的,这些引用类型是基于Object的。最简单的创建对象的方法是

六、总结

关于面向对象的基础知识大概就是这些了。我从最简单的创建一个对象开始,解释了为什么我们需要构造函数与原型,理解了这其中的细节,有助于我们在实际开发中灵活的组织自己的对象。因为我们并不是所有的场景都会使用构造函数或者原型来创建对象,也许我们需要的对象并不会声明多个实例,或者不用区分对象的类型,那么我们就可以选择更简单的方式。

我们还需要关注构造函数与原型的各自特性,有助于我们在创建对象时准确的判断我们的属性与方法到底是放在构造函数中还是放在原型中。如果没有理解清楚,这会给我们在实际开发中造成非常大的困扰。

最后接下来的几篇文章,我会挑几个面向对象的例子,继续帮助大家掌握面向对象的实际运用。

2 赞 4 收藏 1 评论

金沙棋牌官方平台 5

2)原型与in操作符

in单独使用时,通过对象访问到特定属性时返回true,无论该属性存在于实例中还是原型中。hasOwnProperty(),通过对象访问到特定属性时返回true,且该属性存在于实例中。 

var p3 = new Person();
console.log("name" in p3);                //true
console.log(p3.hasOwnProperty("name"));   //false
p3.name = "insist";
console.log(p3.hasOwnProperty("name"));   //true

确定属性到底是存在于对象中,还是存在于原型中。如下函数hasPrototypePropery()返回true表示该属性存在于原型中,而不是存在于实例中。

function hasPrototypeProperty(object, name) {
    return !hasOwnProperty("name") && (name in object);
}

for..in循环,所有通过对象能够访问的,可枚举的(enumerated)属性,既包括存在于实例中的属性,也包括存在于原型中的属性。

for(var prop in p1) {
    console.log(prop);                    //name age job sayName
}

Object.keys(),ECMAScript5的方法,取得对象上所有可枚举的属性,接收一个对象作为参数,返回值是一个包含所有可枚举属性的字符串数组。注意:Person.prototype也是对象。

var keys = Object.keys(Person.prototype);
console.log(keys);             //["name age job sayName"]
var p1 = new Person(); 
console.log(Object.keys(p1));  //[]
p1.name = "get";
console.log(Object.keys(p1));  //["name"]

Object.getOwnPropertyNames(),得到所有实例属性,无论它是否可枚举。

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);     //["constructor", "name", "age", "job", "getName"] 
var keys_p1 = Object.getOwnPropertyNames(p1);
console.log(keys_p1);  //[]

    person.name = 'coco';

 

3)更简洁的原型语法

function Person() {}
Person.prototype = {
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        return this.name;
    }
};
var friend = new Person();
console.log(friend instanceof Person);      //true
console.log(friend instanceof Object);      //true
console.log(friend.constructor == Person);  //false
console.log(friend.constructor == Object);  //false

在上面的代码中,将Person.prototype设置为等于一个对象字面量形式创建的新对象,最终结果相同。但有一个例外:constructor属性不再指向Person了。每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。此时尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确定对象的类型了。

通过如下方式,将constructor手动设置为合适的值。

Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};

    person.name   // 'xhk'

var person = new Object();

4)原型的动态性

在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样如此。

var friend = new Person();
Person.prototype.sayHi = function() {
    console.log("hi");
};
friend.sayHi();

尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,情况就不一样了。

function Person() {}
var friend = new Person();
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};
friend.getName();                  //error

若是创建实例放在重写原型对象之后,则不会报错。

7. 访问器属性不包含数据值,它们包含一堆getter, setter函数(但是他们都不是必须的)。在读取访问器属性的时候,会调用gettter函数,这个函数负责返回有效的值;   在写入访问器属性的时候,调用setter函数,这个函数负责决定如何处理数据。

person.name = "hainan";

5)原生对象的原型

所有原生引用类型(Object、Array、String)都在其构造函数的原型上定义了方法,如:Array.prototype.sort()、String.prototype.subString(), 通过原生对象的原型可以取得所有默认方法的引用,并可以定义新方法。

console.log(typeof Array.prototype.sort);        //function
console.log(typeof String.prototype.substring);  //function

String.prototype.startsWith = function(text) {
    return this.indexOf(text) == 0;
};
var msg = "Hello World";
console.log(msg.startsWith("Hello"));           //true

  1. 访问器属性:configurable,enumerable, get, set。访问器属性不能直接定义,需要使用Object.defineProperty();

person.age = "25";

6)原型对象的问题

缺点一:省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下将取得相同的属性值。

缺点二:原型中所有属性被许多实例共享,这种共享对于函数非常适合。对于包含基本值属性倒也说得过去,因为通过在实例上添加一个同名属性,可以隐藏原型中对应的属性。但对于包含引用类型值得属性来说,问题比较突出。

function Person() { }
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    friends: ["pink", "judy", "sam"],
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("leo");
console.log(person1.friends);           //["pink", "judy", "sam", "leo"]
console.log(person2.friends);           //["pink", "judy", "sam", "leo"]
console.log(Person.prototype.friends);  //["pink", "judy", "sam", "leo"]
person1.age = 35;
console.log(person1.age);           //35
console.log(person2.age);           //23
console.log(Person.prototype.age);  //23

person1的friends属性修改影响了person2的friends,但是person1的age属性修改并未影响person2的age属性。

原因在于:friends数组存在于Person.prototype中而非person1中,因此修改也会通过person2.friends(与person1.friends指向同一个数组)反映出来。而age属性在person1中也存在一份,修改的age属性只是修改person1中的,并不能修改Person.prototype中的age属性。

9.  定义多个属性Object.defineProperties();

直接new出来一个Object对象,之后给这个对象添加属性和方法。

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这样,每个实例都会有自己的一份实例属性的副本,但又同时共享着对方法的引用。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["aa", "bb", "cc"];
}
Person.prototype = {
    constructor: Person,
    sayName: function() {
        console.log(this.name);
    }
};
var person1 = new Person("Brittany", 23, "Web front-end Engineer");
person1.friends.push("dd");     //["aa", "bb", "cc", "dd"]
console.log(person1.friends);
var person2 = new Person("Sam", 26, "Web front-end Engineer");
console.log(person2.friends);   //["aa", "bb", "cc"]
console.log(person1.friends == person2.friends);  //false
console.log(person1.sayName == person2.sayName);  //true

 

 

时间:2014-10-21

地点:合肥

引用:《JavaScript高级程序设计》 

  1. 读取属性的特性: Object.getOwnPropertyDescriptor(); 获取给定属性的描述符,参数一,属性所在的对象,参数二,要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,返回configurable,enumerable, get, set;如果是数据属性,则返回configurable,enumerable,writable, value.

 

11.  创建对象

属性类型

  使用同一个接口创建很多对象,会产生大量的重复代码。

 

  1)工厂模式:用函数来封装以特定接口创建对象的细节,可以创建多个相似的对象。没有解决对象识别问题(即怎样知道一个对象的类型)。

在第五版的javascript中,定义了在内部使用的特性,描述了属性的各种特征,这些特性是javascript引擎使用的,在javascript中我们不能使用它们,为了区别将他们放在了两对方括号中,属性类型分为两种:数据属性和访问器属性。

    function ceratePerson (name, age, gender) {
      var o = {};

 

      o.name = name;
      o.age = age;
      o.gender = gender;
      o.sayName = function() {
        console.log(this.name);
      }
    }

数据属性

    var xhk = createPerson('xhk', 36, '男');

 

    xhk // {name: "xhk", age: 36, gender: "男", sayName: ƒ}

[[Configurable]] 表示能否通过delete删除属性,默认为true

    xhk.sayName()  // xhk

[[Enumerable]] 表示能否通过for-in枚举属性,默认为true

    2)构造函数模式,创建自定义的构造函数,从而定义自定义对象类型的属性和方法。

[[Writable]] 表示能否修改属性的值,默认为true

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

[[Value]] 表示这个属性的值,默认为undefined

      var p1 = new Person('xhk', 36, '男');  // Person {name: "xhk", age: 36, gender: "男", sayName: ƒ}

访问器属性

      构造函数模式和工厂模式的区别:  

 

      a. 没有显示的创建对象;

[[Configurable]] 表示能否通过delete删除属性,默认为true

      b. 直接将属性和方法赋给了this对象;

[[Enumerable]] 表示能否通过for-in枚举属性,默认为true

      c. 没有return语句;

[[Get]] 读取属性是调用的函数,默认值为undefined

      要创建Person的新势力,必须使用new操作符,以这种方式调用构造函数实际上会经历一下四个步骤:

[[Set]] 写入属性时调用的函数,默认值为undefined

      a. 创建一个新对象;

ECMAScript5中的属性修改时通过Object.defineProperty()这个方法,三个参数分别为对象,属性名,描述符对象。直接看例子,value特性

      b. 将构造函数的作用域赋给新对象(因此this指向了这个新对象);

 

      c. 执行构造函数中的代码(为这个新对象添加属性);

var person = {};

      d. 返回新对象;

Object.defineProperty(person,"name",{configurable:true,value:"hainan"})

      p1.constructor   // Person

console.log(person.name);//hainan

      p1 instanceof Object   // true

//等价于var person = {name : "hainan"}

      p1 instanceof Person  // true

再看看writable

   任何函数只要通过new操作符来调用,就可以当做构造函数;而任何函数如果不通过new操作符来调用,跟普通函数没什么两样。

 

  当做普通函数调用:属性和方法都被添加给window对象了。 

var person = {};

  Person('love', 1, 'null');    // window.sayName()   // 'love';

Object.defineProperty(person,"name",{configurable:true,value:"hainan",writable:false});

  Person.call(o, 'lover', 1, null);  // {name: "lover", age: 1, gender: null, sayName: ƒ}

person.name = "allenxing";//修改属性

  Person.apply(o, ['lover', 1, null]);  // {name: "lover", age: 1, gender: null, sayName: ƒ} 

console.log(person.name);//hainan 严格模式下出现错误

  function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = sayName;
  }

下面看看configuration特性

  function sayName() {
    console.log(this.name);
  }

 

  var p1 = new Person('xhk', 36, '男'); // Person {name: "xhk", age: 36, gender: "男", sayName: ƒ}

var person = {};

  p1.sayName()  // 'xhk'

Object.defineProperty(person,"name",{configurable:false,value:"hainan"});

  在全局作用域中定义的函数实际上只能被某个对象使用。

delete person.name;//删除属性

  

console.log(person.name);//hainan 严格模式下出现错误

  3)原型模式: 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype是通过调用构造函数而创建的那个对象实例的原型对象。好处是让所有对象实例共享它所包含的属性和方法。

configuration属性修改成不可以配置的,就是指为false时,就不能再把它修改成可配置的了,看看例子

  function Person () {};

 

  Person.prototype.name = 'xhk';
  Person.prototype.age = 36;
  Person.prototype.gender = 'man';

var person = {};

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

Object.defineProperty(person,"name",{configurable:false,value:"hainan"});

  var p1 = new Person();

Object.defineProperty(person,"name",{configurable:true,value:"hainan"});

  p1.name // 'xhk'

//TypeError: Cannot redefine property: name

  所有实例都是同一组属性和同一个sayName方法。

enumerable特性

  Person.prototype.isPrototypeOf(p1)  // true 判断实例与构造函数的原型对象之间的关系

 

  Object.getPrototypeOf(p1).name  // 'xhk'  获取属性的值

复制代码

  Object.getPrototypeOf(p1)  == Person.prototype   返回的对象实际就是这个对象的原型

var person = {};

  function Person () {};

Object.defineProperty(person,"name",{configurable:true,value:"hainan"});

  Person.prototype.name = 'xhk';
  Person.prototype.age = 36;
  Person.prototype.gender = 'man';
  Person.prototype.sayName = function() {
    console.log(this.name);
  }

for(var pro in person){

  var p1 = new Person ();

  console.log(pro);

  p1.name  // 'xhk'

}

  p1.name = 'coco'; 

//不输出

  p1.name  // 'coco'

//在chrome下默认的enumeration默认为false

  var p2 = new Person(); 

复制代码

  p2.name  // 'xhk'

var person = {};

   delete操作符完全删除实例中的属性,需要继续访问原型中的属性。

Object.defineProperty(person,"name",{configurable:true,value:"hainan",enumerable:true});

  delete p1.name // true

for(var key in person){console.log(key)}

  p1.name  // 'xhk'

//"name"

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

再看一下访问器属性

  p1.name = 'coco'; 

 

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

复制代码

  var o = {
    toString: function() {
      return 'my husband';
    }
  }

var person = {

  for (var prop in o) {
    if(prop === 'toString') {
      console.log('Found toString');
    }
  }   // 'Found toString'

  name : "hainan",

   取得对象上所有可枚举的实例属性使用Object.keys() 接收一个对象作为参数,返回一个包含所有可枚举数据的字符串数组。

  age : 25  

  function Person () {};

}

  Person.prototype.name = 'xhk';
  Person.prototype.age = 36;
  Person.prototype.gender = 'man';
  Person.prototype.sayName = function() {
    console.log(this.name);
  }

Object.defineProperty(person,"year",{

  var keys = Object.keys(Person.prototype);

   get : function(){

  keys  // ["name", "age", "gender", "sayName"]

     return this.age + 1;

  p1.name = 'coco';

   },

  p1.age = 24;

   set :function(value){

  var keys = Object.keys(p1);

     this.age = value + 10;

  keys  // ["name", "age"]

   } 

  得到所有实例属性: Object.getOwnPropertyNames();

});

  Object.getOwnPropertyNames(Person.prototype);   // ["constructor", "name", "age", "gender", "sayName"]

console.log(person.year);//26

  

person.year= 10;

  function Person() {}; 

console.log(person.age);//20

  Person.prototype = {
    name: 'xhk',
    age: 36,
    gender: 'man',
    sayName: function() {
      console.log(this.name);
    }
  }

复制代码

  p1.constructor == Person  // false

ECMAScript5还有一个方法Object.defineProperties()可以定义多个属性,用法如下

  p1.constructor == Object  // true

 

  p1.constructor 不再是Person (因为上面这种语法完全重写了prototype对象),可以使用下面方法将它设置回特定的值。

复制代码

  function Person() {}; 

var person = {}

  Person.prototype = {

Object.defineProperties(person,{

    constructor: 'Person',
    name: 'xhk',
    age: 36, 
    gender: 'man',
    sayName: function() {
      console.log(this.name);
    }
  }

   name : {value : "hainan"},

  1. 创建自定义类型的最常见方式:组合使用构造函数模式和原型模式

   age : {value : 25},

  构造函数用于定义实力属性,原型模式用于定义方法和共享的属性。

   year : {

  function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.friends = ['coco', 'coco1']
  };

       get :function(){

  Person.prototype = {
    constructor: 'Person',
    sayName: function() {
      console.log(this.name);
    }
  }

           return this.age + 1;

  p1.friends.pop('coco1'); 

       },

  p1  // Person {name: "xhk", age: 36, gender: "gender", friends: Array(1)}

       set : function(value){

  var p2 = new Person('xsh', 36, 'gender');

           this.age = value +10;

  p2.friends   // ["coco", "coco1"]

      }

 13. 动态原型模式

   } 

  function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.friends = ['coco', 'coco1'];
    if(typeof this.sayName !== 'function') {
      Person.prototype.sayName = function() {
        console.log(this.name);
      }
    }

});

  }

console.log(person.year);//26

  var p1 = new Person('xhk', 36, 'gender');

person.year = 10;

  p1.sayName()  // 'xhk'

console.log(person.age);//25 注意这里

 

复制代码

  1. 继承

这里我也不知道为啥age的值没有改变,但是下面这样就可以改变了

  接口继承:只继承方法签名

 

  实现继承:继承实际方法

复制代码

  ECMAScript只支持实现继承,实现继承主要是依靠原型链来实现。

var person = {name :"hainan",age:25}

  1)原型链

Object.defineProperties(person,{

  原型链作为实现继承的主要方法

   year : {

  利用原型让一个引用类型继承另一个引用类型的属性和方法。

       get :function(){

  function SuperType() {
    this.property = true;
  } 

           return this.age + 1;

  SuperType.prototype.getSuperValue = function() {
    return this.property;
  }

       },

  function SubType () {
    this.subProperty = false;
  }

       set : function(value){

  SubType.prototype = new SuperType();

           this.age = value + 10;

  SubType.prototype.getSubValue = function() {
    return this.subProperty;
  }

      }

  var instance = new SubType();

   } 

  instance.getSuperValue()  // true

});

  instance.getSubValue()  // false

console.log(person.year);//26

  所有函数的默认原型都是Object实例

person.year = 10;

  原型与实例之间的关系

console.log(person.age);//20 注意这里

  instance instanceof SubType   // true

复制代码

  instance instanceof SuperType  // true

求大神指导下,谢谢

  instance instanceof Object  // true

 

  Object.prototype.isPrototypeOf(instance)  // true

其实上面这些修改属性的特性一般的时候我们是用不到的,理解这些可以帮助我们更好的了解javascript的对象。

  SuperType.prototype.isPrototypeOf(instance)  // true

 

  SubType.prototype.isPrototypeOf(instance)  // true

创建对象

  只要测试的是实例与原型链中出现过的构造函数,结果就会返回true。

 

  给原型添加方法的代码一定要放在替换原型的语句之后。

直接new 

 

 

创建对象有许多种方法,最简单的就是直接new 一个Object对象,之后再添加属性了

 

var person = new Object();

person.name  = "hainan";

person.age = 25;

对象字面量

 

对象字面量是比较常用的创建对象的方法,其实和new差不多,但是写起来比较简洁

 

var person = {

  name : "hainan",

  age : 25  

}

缺点:看起来比较简单,但是假如我们要创建多个对象呢,例如person1,person2.....,直接new和对象字面量都得必须重写代码,也就是会有大量的重复代码。

 

工厂模式

 

工厂模式就是解决了代码的不可复用的问题,工厂模式用一个函数封装了创建对象的过程,看代码

 

复制代码

function personFactory(name,age){

   var obj = new Object();

   obj.name = name;

   obj.age = age;

   return obj;

}

var person1 = personFactory("hainan",25);

var person2 = personFactory("allenxing",26);

复制代码

这样我们需要创建一个person对象时只需要使用personFactory()就可以了,解决了创建多个对象的复杂代码问题。其实可以看出本质还是直接new出来一个Object对象,只不过是用函数封装了起来,用一个return返回这个Object对象。

 

缺点:我们不知道这个对象是具体属于哪个类型的实例,也就是它没有解决对象的识别问题,当然知道对象是Object的实例没有任何实质性意义。

 

构造函数模式

 

构造函数可以创建特定类型的对象, Object和Array等是原生的构造函数,是javascript运行时就存在执行环境当中的,不需要我们自己定义构造函数,当我们需要自定义对象时我们可以自定义构造函数,

 

复制代码

function Person(name,age){

   this.name = name;

   this.age = age;

}

var person1 = new Person("hainan",25);

var person2 = new Person("allenxing",26);

复制代码

 这里我们看到了函数的首字母是大写的,这是一种惯例,为了和其它的函数有区别。要想创建Person的实例,必须使用new操作符,看看new都干些什么了

 

创建一个新的对象

将构造函数的作用域赋值给这个对象,也就是this指向了这个新对象

执行构造函数中的代码

返回这个新对象

person1和person2是Person对象的不同实例,它们有一个相同的constructor属性,并且它们都是Person的实例

 

console.log(person1.constructor == Person);//true

console.log(person2.constructor == Person);//true

console.log(person1 instanceof Person);//true

console.log(person2 instanceof Person);//true

构造函数模式解决了对象识别的问题。

 

我们知道构造函数也是函数,如果不使用new关键字,那么会是怎样的呢,我们知道在全局作用域中this指向的是window,那么直接使用构造函数应该就是将属性或方法添加到了window上了,我们看看是不是这样的情况

 

复制代码

function Person(name,age){

   this.name = name;

   this.age = age;

}

var person = Person("hainan",25);//添加到了window上

console.log(window.name);//hainan

复制代码

没有new实例时,构造函数就普通函数,添加到了this指向的对象中了,在这里this指向的是window。

 

缺点:每当new一个实例出来,每个实例都要重新创建对象的函数,每个实例中的函数是不同的,因为函数也是对象,定义一个函数也就是实例化一个对象。

 

复制代码

function Person(name,age){

   this.name = name;

   this.age = age;

   this.getName = function(){return this.name};//等于this.getName = new Function("return this.name");

}

var person1 = new Person("hainan",25);

var person2 = new Person("allenxing",26);

console.log(person1.getName == person2.getName);//false

复制代码

虽然这个函数是所以实例共享的,但是每个实例中都是一个副本,显然这对性能是不利的。当然我们可以这样干

 

复制代码

function Person(name,age){

   this.name = name;

   this.age = age;

   this.getName = getName;

}

function getName(){return this.name;}

var person1 = new Person("hainan",25);

var person2 = new Person("allenxing",26);

console.log(person1.getName == person2.getName);//true

复制代码

这样就是每个实例都引用了同一个函数,所以是相等的,但是如果方法很多,我们不能把所有的函数就定义在全局作用域当中,这就需要其他的模式来解决了。

 

原型模式

 

原型和原型链我们之前已经讨论过了,这里我们直接学习这个原型模式就好了。

 

复制代码

function Person(){}

Person.prototype = {

  constructor : Person,

  name : "hainan",

  getName : function(){return this.name}  

}

var person1 = new Person();

var person2 = new Person();

console.log(person1.getName == person2.getName);//true

复制代码

这样,原型模式解决了构造函数中的每个实例都有一个方法的副本的性能和内存问题,但是原型模式也有自己的不足

 

缺点:原型中的属性是共享的,这是他的优点也是他的缺点,当属性是引用类型的时候,这个问题就体现出来了。

 

复制代码

function Person(){}

Person.prototype = {

  constructor : Person,

  name : "hainan",

  friends : ["James"],

  getName : function(){return this.name}  

}

var person1 = new Person();

person1.friends.push("Melo");

var person2 = new Person();

console.log(person2.friends);//"James", "Melo"

复制代码

可以看到我们给person1添加一个朋友,结果person2的朋友也添加上了,就是由于所有实例共享原型中的属性。

 

组合模式(构造函数和原型模式)

 

这种是最常用的方式也是大家推荐的方式,构造函数用于定义实例属性,原型模式用于定于方法和共享的属性,这样就会结合两种模式的优点,每个实例都会有一个实力属性的副本,同时共享着对方法的引用,节省了内存。

 

复制代码

function Person(name,age){

  this.name = name;

  this.age = age;

  this.friends = ["James"];

}

Person.prototype = {

  constructor : Person,

  getName : function(){return this.name}  

}

var person1 = new Person("hainan",25);

person1.friends.push("Melo");

console.log(person2.friends);//"James", "Melo"

var person2 = new Person("allenxing",26);

console.log(person2.friends);//"James"

复制代码

动态原型模式

 

所谓的动态原型模式就是将原型模式和构造模式放在了构造函数中,在定义方法时先检查是否存在这个方法,如果存在就什么就不做,若果不存在就在原型上添加这个方法。

 

复制代码

function Person(name,age){

  this.name = name;

  this.age = age;

  if(typeof this.getName != "function"){

    Person.prototype.getName = function(){

      return this.name;

    }

  }

}

var person1 = new Person("hainan",25);

var person2 = new Person("allenxing",26);

复制代码

寄生构造函数模式

 

书上说前面的几种不合适的话就可以使用这种模式,这个模式和工厂模式其实很类似,都是将创建对象的过程封装到一个函数内部,只不过这种模式最后使用了new操作符,new操作符我们以后在详细说说,这里就不说了。

 

复制代码

function Person(name,age){

   var obj = new Object();

   obj.name = name;

   obj.age = age;

   return obj;

}

var person1 = new Person("hainan",25);

var person2 = new Person("allenxing",26);

复制代码

这里是使用了带返回值的构造函数,带的返回值会覆盖构造函数默认的返回值,这个我会写一个关于这个的文章。

 

稳妥构造函数模式

 

书上说的稳妥对象就是没有公共属性,其方法也不引用this对象,看看例子,这个大家还是看看《高级程序设计》上的吧,和前面的几种对比一下就知道为啥叫稳妥了,

 

复制代码

function Person(name,age){

  var obj = new Object();

  obj.getName = function(){

    return name;//没有使用this

  };

  return obj;

}

var person1  = Person("hainan",25);//没有使用new

console.log(person1.getName());

...

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:对象的创建,创建对象

关键词:

上一篇:没有了

下一篇:没有了