js的面向对象

面向对象

1.创建对象

使用new操作符及字面量方法创建,和使用create方式创建对象,3种方式创建出来的都是单个对象,如果我们要创建多个对象时就会产生大量的重复代码。

(1)工厂模式


为了解决创建相似对象的问题。 将创建对象的过程用一个函数包裹起来,每次创建一个对象,都是运行一次函数。
定义一个变量,运行函数,将返回值赋值给变量。

问题:

创建出来的就是一个对象,无法识别对象的类型。

(2)构造函数模式


构造函数用来创建特定类型的对象。像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而自定义对象类型的属性的方法。
与工厂模式的区别:
* 没有显示的创建对象;
* 直接将属性和方法赋给了this对象;
* 没有return语句。

使用new操作符创建对象实例要经历的4个步骤:
(1)创建一个对象;
(2)将构造函数的作用域赋给新对象(this就指向了这个新对象);
(3)执行构造函数中的代码(为这个新对象添加属性);
(4)返回新对象。

构造函数解决工厂模式问题的方式:
每个对象都有一个constructor属性,该属性是一个指针,指向当前对象的构造函数。在工厂模式下创建的对象,当调用对象的constructor时,只能返回一个Object。如果是以构造函数模式创建的对象,它的constructor对象指向了实际的构造函数。

alert(person1.constructor == Person);  //true
alert(person2.constructor == Person);  //true

检测基本数据类型时使用tyoeOf,但是查看对象时,总是返回Object,也就没有什么意义。
使用instanceof检测对象会更可靠一些。

this指向4类:(应该讲在构造函数模式前面)

(1)作为纯函数的调用,指向全局作用域,在浏览器中指向window。

function info(){
    console.log(this);
}

(2)作为对象的属性和方法调用的时候指向对象
(3)作为构造函数调用,指向的是创建出来的新对象
(4)强制指向一个对象,使用apply()call()指向的对象。(apply的第二个参数用的是数组,call用的是使用逗号分割的参数)。

构造函数的问题

每个方法都要在实例上重新创建一遍。
解决方式,将复用的方法在全局作用域中创建(问题,无法保证不重复)。

(3)原型模式


每一个函数都有一个prototype(原型)属性,指向一个对象,我们将prototype对应的对象称为原型对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。原型对象的constructor指向这个对象的构造函数,两个指针形成一个闭环。

1. 理解原型对象

创建了自定义的构造函数后,其原型对象默认只会取得constructor属性;至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。ES5中这个指针叫做[[Prototype]]。虽然在脚本上没有标准的方式访问[[Prototype]],但是Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。

要明确一点的就是这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。

2.原型对象的方法

原型对象也有prototype属性,原型链的根节点是ObjectvalueOf()toString()就定义在Object的原型对象上。
所有的对象都可以调用valueOf()toString()方法,原因是这两个方法是定义在原型对象上的。
Object的原型对象上还有3个方法:
(1)isPrototypeOf()
作用是看看调用这个方法的对象是不是这个方法的对应参数的原型。


console.log(Person.prototype.isPrototypeOf(person1)); //true
console.log(Person.prototyoe.isPrototyoeOf(person2)); //true

(2)hasOwnProperty()
作用是判断一个属性是存在实例中还是存在原型中。这个方法只在给定属性存在于对象实例中时,才会返回true。
(3)ES5中的Object.getOwnPropertyDescriptor()方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用Object.getOwnPropertyDescriptor()方法。

3.原型与in操作符

有两种方式使用in操作符,单独使用和在for-in循环中使用。
单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论实在实例中还是在原型中。

(1)hasOwnProperty() 和in操作符合用,能够确定是原型中的属性还是实例中的属性。
(2)使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的属性,既包含实例中的属性,也包含原型中的属性。
(3)所有开发者定义的属性都是可枚举的。

  1. Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。
  2. for...in 语句以任意顺序遍历一个对象的可枚举属性(包括原型中的可枚举属性)。对于每个不同的属性,语句都会被执行。
  3. Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。
  4. hasOwnProperty() 方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。

获取实例自身的属性

第一种方式,使用for-in取到所有的属性,再使用es5中的hasOwnProperty()方法.
第二种方式,使用Object.getOwnPropertyNames()

获取所有属性

第一种方式,使用for-in循环(es3下)。
第二种方式,使用Object.keys() 在es5 下。

获取原型对象上的属性

第一种方式,查出所有属性和自身属性后做差。
第二种方式,用prototype获取实例的原型对象,再使用Object.getOwnPropertyNames()查出在原型对象上的属性。

上述方法所获取到的属性都是可枚举的(一般开发人员自己定义的属性都是可枚举的),但是在Object的原型对象上的属性都是不可枚举的。如toString()valueOf(),特殊的是在es3下,constructor是可枚举的,在es5下是不可枚举的。


一个bug,在早期的IE版本中,即屏蔽不可枚举属性的实例属性不会出现在for-in循环中。
(说人话就是,自己定义的方法如果和Object的原型中的不可枚举方法重名,则认为这个方法是不可枚举的)

var o = {
    toString:function(){
        return 'my object';
    }
};
for(var prop in o){
    if(prop == 'toString'){
        alert("found toString");
    }
}
4.简单的原型语法

更简单的方法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。

function Person(){
}
Person.prototype = {
    name:"lulu",
    age:28,
    job:"software engineer",
    sayName:function(){
        alert(this.name);
    }
};

这样写法会使constructor属性指向Object。而且

var person1 = new Person();
console.log(person1 instanceof Object);  //true
console.log(person1 instanceof Person);  //true
console.log( person1.constructor == Person);   //false
console.log( person1.constructor == Object);   //true

解决方式是认为的给constructor添加一个指向。

Person.prototype = {
    constructor:Person,
    name:"lulu",
    age:28,
    job:"software engineer",
    sayName:function(){
        alert(this.name);
    }
};

但是这样做会有问题哦
在默认的情况下,constructor是不可枚举的,但是重写constructor后,会变成可枚举的。我们又要重新将constructor设置为不可枚举的。

Object.defineProperty(Person.prototype,"constructor",{
    enumerable:false,
    value:Person
    });

so, 在开发时如果涉及到原型模式,我们都会按照传统的方法书写,保证准确性。

5.原型的动态性

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

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

但是如果我们用简单的原型语法重写原型对象,就会使构造函数与最初原型之间的联系。

请记住,实例中的指针仅指向原型,而不指向构造函数。

function Person(){
}
var friend = new Person();

Person.prototype = {
    constructor:Person,
    name:"lulu",
    age:28,
    job:"software engineer",
    sayName:function(){
        console.log(this.name);
    }
};

friend.sayName(); //error

对象分为三类:错误对象,常用对象(8种),内置对象。

6.原生对象的原型

常用对象的方法都是通过原型创建的,所有原生引用类型都在其构造函数的原型上定义了方法。
通过原生对象的原型,不仅可以取得所有默认方法的引用,因此可以随时添加方法。

7.原生对象的问题

最大的问题是由其共享的本性所导致的。

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
集两种模式之长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值