RYF javascript笔记3
4. 面向对象编程
4.1 面向对象编程概述
4.1.1 简介
4.1.1.1 构造函数
js没有“类”,而改用构造函数作为对象的模板。
var Vehicle = function() {
this.price = 1000;
};
构造函数是一个正常函数。但是:它使用new命令调用;函数体内部使用this代表要生成的对象实例。
4.1.1.2 new命令
new命令执行构造函数,返回一个实例对象。
使用new调用构造函数时,传多少个参数都可以,甚至可以可以省略括号。
var Car = function(p) {
this.price = p;
};
var c1 = new Car(); //price:undefined
var c2 = new Car(1); //price:1
var c3 = new Car(1,2); //price:1
var c2 = new Car; //price:undefined
不使用new调用构造函数时,构造函数就变成了普通函数,并不会生成实例对象。而且由于this这时代表全局对象,将造成一些意想不到的结果。
var c4 = Car(); //c4:undefined
因此,为了保证构造函数必须与new命令一起使用,可以在构造函数内部使用严格模式。
var Lorry = function() {
"use strict" // 严格模式
this.price = 1;
};
var l = Lorry(); // 无法设置未定义或 null 引用的属性“price”
或者在构造函数内部判断是否使用了new命令。
function Vehicle (p) {
if (!(this instanceof Vehicle)) {
return new Vehicle(p);
}
this._p = p;
}
var v1 = new Vehicle(9); // Vehicle里的this指向的是new出来的对象
var v2 = Vehicle(9); // Vehicle里的this指向的是window对象
4.1.1.3 instanceof运算符
instanceof用来确定对象是否是某个构造函数的实例。
在JavaScript之中,所有对象都有对应的构造函数。
[1, 2, 3] instanceof Array // true
({}) instanceof Object // true
但是,由于原始类型的值不是对象,所以不能使用instanceof运算符判断类型。
"" instanceof String // false
1 instanceof Number // false
如果存在继承关系,那么instanceof运算符对这些构造函数都返回true。
var a = [];
a instanceof Array // true
a instanceof Object // true
4.1.2 this关键字
4.1.2.1 涵义
this是指函数当前的运行环境。
难点是:JavaScript支持运行环境动态切换。
function f(){ console.log(this.x); };
var a = {x:'a'};
var b = {x:'b'};
a.m = f;
b.m = f;
a.m() // a
b.m() // b
4.1.2.2 this的使用场合
1、全局环境
在全局环境使用this,它指的就是顶层对象window。
this === window // true
function f() {
console.log(this === window); // true
}
2、构造函数
构造函数中的this,指的是实例对象。
3、对象的方法
将对象的方法赋值给另一个对象,会改变this的指向。
var o1 = {m:1};
o1.f = function (){ console.log(this.m);};
o1.f() // 1
var o2 = {m:2};
o2.f = o1.f
o2.f() // 2
o1.f() // 1
将对象内部的方法赋值给一个变量,也可能改变this的指向。
var a = {
b : {
m : function() {
console.log(this.p);
},
p : 'Hello'
}
};
var t = a.b.m;
t(); // undefined
a.b.m(); // Hello
4.1.2.3 使用注意点
1、避免多层this
2、避免数组处理方法中的this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(item);
console.log(this.v);
});
}
}
o.f(); // f函数里的item正确,但this.v是undefinded
解决问题的一种方法,是使用中间变量。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(this.v);
});
}
}
3、避免回调函数中的this
回调函数中的this往往会改变指向,最好避免使用。
4.1.3 固定this的方法
4.1.3.1 call方法
函数的call方法,可以改变指定该函数内部this的指向,然后再调用该函数。
var n = 123;
var o = { n : 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(o) // 456
如果this所要指向的对象,设定为null或undefined,则等同于指向全局对象。
call方法的完整使用格式如下:
func.call(thisValue, arg1, arg2, ...)
4.1.3.2 apply方法
apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。它的使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
apply的第一个参数如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组。
function f(x,y){ console.log(x+y); }
f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2
f.apply(null,[1,1,1]) // 2
f.apply(null,[]) // NaN
4.1.3.3 bind方法
bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。它的使用格式如下。
func.bind(thisValue, arg1, arg2,...)
4.2 封装
4.2.1 prototype对象
4.2.1.1 构造函数的缺点
构造函数定义的属性和方法是属于具体对象的,无法共享。(当然了)
4.2.1.2 prototype属性的作用
js的每个对象都有一个原型对象(prototype)。
- 定义在prototype上面的属性和方法,能被所有实例对象共享。
- 修改prototype对象会影响所有实例对象。
- 实例优先调用自身的属性或方法,然后才是prototype的属性或方法。
示例:
function Animal () {
}
var cat1 = new Animal();
var cat2 = new Animal();
Animal.prototype.color = "white";
cat1.color // 'white'
cat2.color // 'white'
Animal.prototype.color = "yellow";
cat1.color // 'yellow'
cat2.color // 'yellow'
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
4.2.1.3 原型链
从自身到原型,再到原型的原型,这样就形成了原型链。直至Object.prototype,它的原型是null,因此原型链结束。
function MyArray (){}
MyArray.prototype = new Array();
var mine = new MyArray();
mine.push(1, 2, 3);
mine.length // 3
mine instanceof Array // true
4.2.1.4 constructor属性
prototype对象有一个constructor属性,默认指向prototype对象的构造函数。
function P() {}
P.prototype.constructor === P // true
4.2.2 Object.getPrototypeOf方法
返回对象的原型。
// 空对象的原型是Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true
// 函数的原型是Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
// 假定F为构造函数,f为F的实例对象
// 那么,f的原型是F.prototype
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true
4.2.3 Object.create方法
Object.create方法用于生成新的对象,可以替代new命令。它接受一个原型对象作为参数,返回一个新对象,后者完全继承前者的属性。
var oldObj = {
id: 123,
"name": "Hello"
};
var newObj = Object.create(oldObj)
4.2.4 isPrototypeOf方法
isPrototypeOf方法用来判断一个对象是否是另一个对象的原型。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
4.3 继承
4.3.1 proto属性
在Object对象的实例有一个非标准的proto属性,指向该对象的原型对象,即构造函数的prototype属性。
var o = new Object();
o.__proto__ === o.constructor.prototype // true
o.__proto__ === Object.getPrototypeOf(o) // true
4.3.2 属性的继承
属性有两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。
4.3.2.1 对象的原生属性
对象的原生属性,可以用Object.getOwnPropertyNames获得。
Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]
只获原生属性中可枚举的,用Object.keys。
Object.keys(Date)// []
判断对象是否具有某个属性,使用hasOwnProperty。
Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false
4.3.2.2 对象的继承属性
用Object.create创建的对象,会继承所有原型对象的属性。
var proto = { p1: 123 };
var o = Object.create(proto);
o.p1 // 123
o.hasOwnProperty("p1") // false
4.3.2.3 获取所有属性
判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。
"length" in Date // true
"toString" in Date // true
可用for-in循环所有可枚举属性。
var o1 = {p1:123};
var o2 = Object.create(o1,{
p2: { value: "abc", enumerable: true }
});
for (p in o2) {
console.log(p);
}
// p2
// p1