javascript 面向对象编程
实物的抽象--对象
当实物抽象成"对象",实物之间的关系 就变成了"对象"之间的关系
对象是一个模型,封装了"属性"(property)和"方法"(method)
所谓"属性"就是对象的状态,所谓"方法"就是对象的行为
类和构造函数
面向对象就是要生成对象,生成对象就需要一个模板,用来表示某一类实物的共同特征,然后"对象"根据这个模板生成实例."类"就是对象 的模板,对象就是"类"的实例.
javascript没有"类",只有用构造函数(constructor)作为对象的模板.构造函数的最大特点就是函数体内使用了this关键字,代表了所要生成的对象实例.生成对象 的时候,必需用new命令,调用 构造函数.
用构造函数生成一个对象实例后, 构造函数内部的this代表被生成的实例对象.
基本用法
//构造函数
var con_obj = function(){
this.speed = 100;
};
//实例化对象
var v = new con_obj();
console.log(v.speed);//100
更简单的原型语法
function Person(){}
Person.prototype={
name:'anry',
age:27,
job:"web",
sayName:function(){
alert(this.name);
}
};
var key = Object.keys(Person.prototype);
alert(key);
var p1 = new Person();
p1.name = 'coco';
p1.age= 23;
var p1key = Object.keys(p1);
alert(p1key);
注意:如果不使用new而直接调用构造函数,this代表的是全局对象,构造函数就变成了普通函数,并不会生成实例对象.
instanceof运算符
运算符用来确定一个对象是否为某个构造函数的实例.
//构造函数
var con_obj = function(){
this.speed = 100;
};
//实例化对象
var v = new con_obj();
console.log(v instanceof con_obj);//true
instanceof运算符的左边放置对象,右边放置构造函数.在javascript中,只要是对象,就有对应的构造函数.因此,instanceof运算符可以用来判断值的类型.
[] instanceof Array //true
({}) instanceof Object //true
注意:原始类型的值不是对象,所以不能用instanceof运算符来判断类型,但可以用 typeof 可判断.
instanceof运算符实质就是找出instanceof运算符左侧的实例对象的原型键上各个原型的constructor属性,然后确定这些属性中是否包含instanceof运算符右侧的构造函数.
this
javascript允许动态调用对象的属性.如果a对象和b对象都有一个m属性,javascript在调用m时,会动态切换,这时可能会属a也可以属b,这就要靠this来办到.
如果处在全局环境,this就指顶层对象 (window),如果不处在全局环境,this就指对属性求值时所在的对象.
全局环境
this === window //true
构造函数
构造函数中的this,指的是实例对象
var con_obj = function(p){
this.p = p;
}
con_obj.prototype.m = function(){
return this.p;
}
var test_o = new con_obj('test');
test_o.p //test
test_o.m() // test
上面代码定义了构造函数con_obj.由于this指向实例对象,所以在构造函数内部定义了this.p.就相当于定义的实例对象里有一个p属性,然后m方法就可以返回这个实例化的P属性.
普通函数
普通函数内部的this,指的是函数运行时所在的对象.
function f(){
console.log(this.m);
}
var m = 1;
f(); //1
//函数f是在全局环境中运行的,this指向的就是全局对象window,所以可以读取全局变量m的值.
function f(){
console.log(this.m);
}
var obj = {m:1};
obj.f = f;
obj.f() //1
var obj2 = {m : 2};
obj2.f = f;
obj2.f() //2
//当f在obj下运行时,this指向的是obj, 当在obj2下运行时,this指向的是obj2.
有时,某个方法m位于多层对象的内部,为了避免this指向全局对象,可以只将m函数所在的对象赋值给上层对像.如下
var a = {
b:{
m:function(){
console.log(this.p)
},
p:'test'
}
};
var h = a.b;
h.m();//test
结论
如果是在全局环境下运行,就代表全局对象window; 如果是在某个对象中运行,就代表该对象. 函数中使用多个this的方法如下:
var o = {
f1: function(){
console.log(this);//object
var that = this;
var f2 = function(){
console.log(that);//object
}();
}
}
o.f1();
//上面代码定义了变量that,固定指向外层的this,
然后在内层使用that,就不会发生this的改变.
call 方法
函数的call方法可以改变this指向的对象.然后再调用该函数.
fun.call(contest,[arg1],[arg2],...);
//call方法的第一个参数就是this所要指向的那个对象,
后面的参数则是函数调用时所需要的参数,
如果this所要指向的那个对象,
设定为null或undefined,则等同指定全局对象.
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
apply方法
apply方法与call方法的作用类似,也是改变this指向的对象. 区别在于apply方法的第二个参数是一个数组,该数组的所有成员 依次作为参数,传入原函数.
fun.apply(context,[arg1,arg2,...]);
function f(x,y){ console.log(x+y); }
f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2
//上面的f函数本来接受两个参数,
使用apply方法以后,就变成可以接受一个数组作为参数
利用apply这一点可以找出数组中最大元素的函数.
//找出数组中最大元素
var a = [10,2,3,4,15,16,9];
Math.max.apply(null,a);
// 16
通过apply方法,利用构造函数Array将数组的空元素变成undefined。
Array.apply(null, ["a",,"b"])
// [ 'a', undefined, 'b' ]
空元素与undefined的差别在于,数组的foreach方法会跳过空元素,
但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果。
var a = ["a",,"b"];
function print(i) {
console.log(i);
}
a.forEach(print)
// a
// b
Array.apply(null,a).forEach(print)
// a
// undefined
// b
利用数组对象的slice方法,可以将一个类似数组的对象
(比如arguments对象)转为真正的数组。但被处理的对象必须有length属性,
以及相对应的数字键.
Array.prototype.slice.apply({0:1,length:1})
// [1]
Array.prototype.slice.apply({0:1})
// []
Array.prototype.slice.apply({0:1,length:2})
// [1, undefined]
Array.prototype.slice.apply({length:1})
// [undefined]
bind方法
bind 方法就是单纯了将函数体内的this绑定到某个对象,然后返回一个新函数, 它他call方法和apply方法更进一步的是,除了绑定this以外,还可以绑定原函数的参数.
var o1 = new Object();
o1.p = 123;
o1.m = function (){
console.log(this.p);
};
o1.m() // 123
var o2 = new Object();
o2.p = 456;
o2.m = o1.m;
o2.m() // 456
o2.m = o1.m.bind(o1);
o2.m() // 123
上面代码使用bind方法将o1.m方法绑定到o1以后,在o2对象上调用o1.m的时候,o1.m函数体内部的this.p就不再到o2对象去寻找p属性的值了。
如果bind方法的第一个参数是null或undefined,等于将this绑定到全局对象,函数运行时this指向全局对象(在浏览器中为window)。
function add(x,y) { return x+y; }
var plus5 = add.bind(null, 5);
plus5(10) // 15
bind方法每运行一次就会返回一个新函数,这个会产生一些问题,如监听事件时,因click事件 绑定bind方法后,会生成一个匿名函数.这样会导致无法取消绑定.
var o = {};
o.p = 123;
o.m = function(){
console.log(this.p);
};
var listener= o.m.bind(o);
var ele = document.getElementById("mydiv1");
ele.addEventListener('click', listener);
// ...
ele.removeEventListener('click', listener);
对于那些不支持bind方法的老式浏览器,可以自行定义bind方法。
if(!('bind' in Function.prototype)){
Function.prototype.bind = function(){
var fn = this;
var context = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
return function(){
return fn.apply(context, args);
}
}
}
除了用bind方法绑定函数运行时所在的对象,还可以使用jQuery的$.proxy方法,它与bind方法的作用基本相同。
$("#button").on("click", $.proxy(o.f, o));
上面代码表示,$.proxy方法将o.f方法绑定到o对象。
bind方法改写其它方法
var push = Function.prototype.call.bind(Array.prototype.push);
var pop = Function.prototype.call.bind(Array.prototype.pop);
var a = [1 ,2 ,3];
push(a, 4)
a // [1, 2, 3, 4]
//等同于
a.push(4);
pop(a)
a // [1, 2, 3]