js面向对象:创建对象的几种方式

本文介绍了JavaScript中五种创建对象的方法,包括普通模式、工厂模式、构造函数模式、原型模式及原型链的应用。每种模式均有其特点与适用场景,如构造函数模式适合创建自定义对象类型,而原型模式则用于实现方法和共享属性。
1、普通模式

//var person new Object();(new Object 用{}代替)

var person = {};
person.name = 'dd';
person.color = 'red';
person.sayHi = function(){
 alert(this.name);// this.name等价于 person.name
};
调用:直接用person.name、person.sayHi()
缺点:相似的对象需多次创建,产生大量的重复代码
 
2、工厂模式
function createPerson(name, color) {
    var person = {};
    person.name = name;
    person.color = color;
    person.sayHi = function() {
        alert(this.name);
    }
    return person;
}
调用:var person1 = createPerson(‘zhangsna’, ‘red’);
person1.name; 、 person1.sayHi();
优点:函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的person对象,可以无数次地调用这个函数,每次都会返回一个包含三个属性、一个方法的对象。在创建多个相似对象时,就可省去重复代码
缺点:没有解决对象识别的问题(即不知道对象的类型)
 

3、构造函数模式

function Person(name, color) {
    this.name = 'dd';
    this.color = 'red';
    this.sayHi = function() {
        alert(this.name);
    }
}
 
调用:var person  = new Person();new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号var person  = new Person
优点:可以创建自定义的构造函数,定义自定义对象类型的属性和方法。
缺点:每个方法都要在每个实例上重复创建一遍,
说明:构造函数也是函数,与其他函数唯一的区别就在于调用它们的方式不同,任何函数通过new操作符来调用就可作为构造函数。
如果忘了使用new命令,直接调用构造函数,构造函数就变成了普通函数,并不会生成实例对象。
为了保证构造函数必须与new命令一起使用,一个解决办法是,在构造函数内部使用严格模式,即第一行加上“use strict”。
function Fubar (foo, bar) {
  "use strict"
  this._foo = foo;
  this._bar = bar;
}
 
Fubar();
// TypeError: foo is undefined
 
上面代码的Fubar为构造函数,use strict命令保证了该函数在严格模式下运行。由于在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,导致不加new调用会报错(JavaScript不允许对undefined添加属性)。
 
另一个解决办法,是在构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。
function Fubar (foo, bar) {
  if (!(this instanceof Fubar)) {
    return new Fubar(foo, bar);
  } 
  this._foo = foo;
  this._bar = bar;
}
instanceof运算符用来确定一个对象是否为某个构造函数的实例(用来判断对象的类型)
""instanceofString   //无效
 
1   instanceofNumber   //无效
 

4、 原型模式

         function Person() {

}

Person.prototype  =  {

name : ‘dd’,

color 'red',
    sayHi :function() {
       alert(this.name);
    }

}
优点:每个函数都有一个prototype属性(原型属性),这个属性是一个指针,指向一个对象(原型对象),原型对象中的属性和方法是所有函数实例共享的

 

给原型对象添加属性和方法:

1)Person.prototype  =  {

name : ‘dd’,

color 'red',
    sayHi:function() {
       alert(this.name);
             }

}

2)

Person. prototype.name = ‘dd’;

Person. prototype.color = ‘red’;

Person. prototype.say Hi = function(){

 

};

调用: var person = new Person();

person.name 、person.sayHi();

原型:无论什么时候,只要创建了一个新函数,根据特定的规则,就会给该函数创建一个prototype属性,默认情况下prototype属性会自动获得一个constructor(构造函数)属性。constructor这个属性包含一个指向prototype属性所在函数的指针(Person. prototype.constructor = Person).

当调用构造函数创建一个新实例后,该实例的内部包含一个指针(内部属性_proto_),指向构造函数的原型属性

 

缺点:当原型中的属性出现引用类型值的时候,就会出现如下问题:

function Person() {

}

Person.prototype  =  {

name : ‘dd’,

color 'red',

friends:[‘Shelby’, ’Court’],
    say:function() {
       alert(this.name);
    }

}

var person1 = new Person();

var person2 = new Person();

person1.friends.push(‘Van’);
alert(person1.friends); //输出 “Shelby, Court, Van”

alert(person2.friends); //输出 “Shelby, Court, Van”

person1.name = 'ddd';
alert(person1.name);//输出 ddd
alert(person2.name);//输出 dd

如果是基本类型的话实例person1的name会调用自己实例中设置的属性,person2则还是运用原型中的name属性。

由于friends数组存在于Person.prototype而非person1中,所以person1.friends与person2.friends指向与同一个数组,这样的结果并不是我们想要的,此情况下可以采用构造函数模式与原型模式相结合,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

function Person(name, color) {
    this.name = name;
    this.color = color;
    this.friends = ['Shelby', 'Court'];
}
Person.prototype = {
    sayHi : function() {
        return this.name;
    }
}
var person1 = new Person('a', 'red');
var person2 = new Person('b', 'red');
person1.friends.push('Van');
alert(person1.friends); //输出 “Shelby, Court, Van”
alert(person2.friends); //输出 “Shelby, Court”

 

原型链:(基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法)

function SuperType() {
    this.superName = "a";
}
SuperType.prototype.getSuperName = function() {
    return this.superName;
}
function SubType() {
    this.subName = "b";
}
SubType.prototype = new SuperType();
SubType.prototype.getSubName = function(){
    return this.getSubName();
}
var subType = new SubType();
console.log(subType.getSuperName());

 

 

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined,例子:

    function foo() {
            this.add = function (x, y) {
                return x + y;
            }
        }

        foo.prototype.add = function (x, y) {
            return x + y + 10;
        }

        Object.prototype.subtract = function (x, y) {
            return x - y;
        }

        var f = new foo();
        alert(f.add(1, 2)); //结果是3,而不是13
        alert(f.subtract(1, 2)); //结果是-1

我们可以赋值任何类型的对象到原型上,但是不能赋值原子类型的值,比如如下代码是无效的

function Foo() {}
Foo.prototype = 1; // 无效
 

hasOwnProperty是Object.prototype的一个方法,他能判断一个对象是否包含自定义属性而不是原型链上的属性,因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。

// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
console.log(foo.bar); // 1
console.log('bar' in foo); // true

console.log(foo.hasOwnProperty('bar')); // false
console.log(foo.hasOwnProperty('goo')); // true
 

同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰

Object.prototype.bar = 1;

var foo = {moo: 2};
for(var i in foo) {
    console.log(i); // 输出两个属性:bar 和 moo
}

我们没办法改变for in语句的行为,所以想过滤结果就只能使用hasOwnProperty 方法,代码如下:

// foo 变量是上例中的
for(var i in foo) {
    if (foo.hasOwnProperty(i)) {
        console.log(i);
    }
}

由于我们使用了 hasOwnProperty,所以这次只输出 moo。如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。

推荐使用 hasOwnProperty

注:此文章是同事所给资料复制过来的

转载于:https://www.cnblogs.com/mandy-dyf/articles/4539076.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值