1,面向对象
1.1构造函数
在ES6之前,JavaScript 不是基于类来创建,而是基于构造函数(constructor)和原型链(prototype)来创建对象。 构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new关键字一起使用。我们把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。 在JS中,使用构造函数时要注意以下两点:
- 构造函数用于创建某一类对象,其首字母要大写
- 构造函数要和new关键字一起使用才有意义
new关键字在创建对象时做以下几件事件: - 在内存中创建一个新空对象
- 让this指向到这个新空对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象
1.2对象成员
对象成员分为实例成员和静态成员
1.2.1实例成员
实例成员就是构造函数内部通过this添加的成员。如上例代码中name、age、say就是实例成员。实例成员只能通过实例化的对象来访问。
1.2.2静态成员
静态成员是在构造函数本身上添加的成员。如下列代码中 gender就是静态成员。静态成员只能通过构造函数来访问。
1.3instanceof
instancpof 是一个JavaScript的关键字,用于判断是否为某个对象的实例,如果是则返回true,否则返回false。
1.4constructor
每一个对象在创建的时候都会自动拥有一个构造函数属性constructor。 constructor属性是继承自原型对象,指向了构造函数的引用。
1.5原型对象
构造函数很方便,但是存在内存空间浪费的问题。而JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
1.5.1原型关系
- 构造函数:用来初始化新创建对象的函数。上例中的Foo就是构造函数。它会自动给构造函数赋予一个叫prototype的属性,该属性指向了实例对象的原型对象。
- 实例对象:通过new 创建的对象就是实例对象,如上例中的f1就是实例对象,实例对象可以创建多个并且是独立的。每一个原型对象中都有一个_proto_对象。每个实例对象都有一个constructor属性,这个属性是继承自父类,它指向了当前的构造函数。
- 原型对象:Foo.prototype
①在JS中万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此它们都会具有对象共有的特点。即:对象具有属性proto_,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。 ②方法(Function)是一个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性一原型属性(prototype),这个属性是一个指针指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。
总结:
- 构造函数Foo()的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。
- 原型对象Foo.prototype保存着实例共享的方法,有一个constructor指回构造函数。
- 对象f1是Foo对象的实例,这个对象有一个_proto_ 属性,指向构造函数的原型对象,用于访问原型对象的所有方法。
1.5.2原型属性
proto_和constructor属性是对象所独有,而prototype属性是函数所独有的。但由于在JS中函数也是一种对象,因此函数也拥有_proto_和constructor属性。 原型继承的作用:
- JS继承的机制:通过原型对象实现继承。其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠proto_继承而来的。
- 原型对象的作用就是定义所有实例对象共享的属性和方法。
1.5.3原型链
原型链:对象的原型 ->原型的原型 ->原型的原型的原型=> null根据原型链查找,如果一层一层往上查找,所有对象的原型最终都可以寻找到Object.prototype (Object构造函数的prototype)。 总结:所有的对象都继承了Object.prototype 上的属性和方法。 读取属性和方法的规则:
JS引擎会先寻找对象本身的属性和方法,如果找不到就到原型对象上去寻找,这样一层一层寻找,直到找到为止,如果找不到就抛出异常。
1.5.4原型总结
<script>
function Foo() {
}
Foo.prototype.name = 'jock';
var f1 = new Foo();
console.log(Foo.prototype === f1._proto_);
console.log(Foo.prototype.constructor === Foo);
console.log(f1.constructor === Foo);
</script>
总结:
- 需要牢记两点:_proto_和constructor属性是对象所独有的;②prototype属性是函数所独有的,由于函数也是一种对象,因此函数也拥有_proto_和constructor属性。
- _proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的 proto_属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
- prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.proto_=== Foo.prototype.
- constructor属性的含义是指向该对象的构造函数,所有函数最终的构造函数都指向Function。l
1.6对象创建
在JS中共有五种方式创建对象
1.6.1对象字面量
- new构造函数
var obj = new Object();
obj.name = '张三';
console.log(obj);
- 字面量
var person = {
name: '张三',
age: 18
}
console.log(preson);
console.log(person.name);v
- create
在ES5中定义了另一种创建对象的方法:从一个实例对象来生成另一个实例对象,使用Object.create()方法来创建。
<script>
var person = {
name: 'zhang',
age: 18,
say: function () {
console.log(name);
}
}
var p = Object.create(person);
console.log(p);
p.say();
</script>
1.6.2工厂模式
如果创建单个对象,适字面量的方式来创建是非常方便快捷的。但是,如果我们要创建很多相似的对象,则使用工厂模式是一个比较好的选择。
<script>
function createPerson(name,age) {
this.name = name;
this.age = age;
}
var p1 = new createPerson('wang',20);
var p2 = new createPerson('zhang',21);
</script>
1.6.3构造函数模式
可以通过使用构造函数模式来解决工厂模式中存在的无法解决对象识别的问题。
<script>
function createPerson(name,age) {
this.name = name;
this.age = age;
this.showName = function () {
console.log(this.name);
}
}
var p1 = new createPerson('wang',20);
var p2 = new createPerson('zhang',21);
</script>
1.6.5构造函数拓展模式
构造函数拓展模式解决了构造函数模式中存在的浪费资源的问题。它的设计思想是把方法抽取出来形成一个独立的方法。
<script>
function createPerson(name,age) {
this.name = name;
this.age = age;
this.showName = showName;
}
function showName() {
console.log(this.name);
}
var p1 = new createPerson('wang',20);
var p2 = new createPerson('zhang',21);
</script>
构造函数拓展模式解决了系统内存占用的问题,但同时又产生了新的问题:由于函数定义到了对象外部,此时提升为了一个全局函数。是对象中需要有多个方法,是否需要都定义到全局中呢?如果是会污染全局空间,如果不是,那又如何解决?使用寄生构造函数模式来解决。
1.6.5寄生构造函数模式
寄生构造函数模式接合了工厂模式和构造函数模式。 创建一个函数,函数体内实例化一个对象,并且将对象返回,在外部的使用new来实例化对象。
<script>
function Person(name,age) {
var o = new Object();
o.name = name;
o.age = age;
o.showName = function () {
console.log(this.name);
}
return 0;
}
var p1 = new Person('wang',20);
var p2 = new Person('zhang',21);
</script>
|