javascript面向对象
一. 什么是对象?
面向对象编程(Object Oriented Programing,缩写为OOP)是目前主流的编程范式,它将真实世界各种复杂的关系,抽象为一个个的对象,然后由对象之间的分工与合作,完成真实世界的虚拟。
每一个对象都是功能中心,具有明确的分工,可以完成接受信息,处理数据,发出信息等任务,因此面向对象编程具有灵活,代码可复用,高度模块化等特点。容易维护和开发比起由一系列函数或指令组成的传统的过程式编程(procedura programming),更适合多人合作的大型项目。
那么面向对象(object)到底是什么?
1. 对象是单个实物的抽象。
一本书、一个人、一辆车都可以是对象。
一个数据库、一张网页、一个服务器甚至与服务器的连接也可以是对象。
当实物抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。
- 对象是一个容器,封装了属性(property)和方法(method)。
属性是对象的状态,方法是对象的行为(完成某种任务)。比如我么可以把动物抽象为一个animal对象,使用属性记录具体是哪种动物。使用方法来表示动物的某种行为(奔跑、捕猎、休息等)。
二、构造函数
面向对象的第一步,就是要生成对象。
前面说过,对象是单个实物的抽象,通常需要一个的模板,表示某一类实物的共同特征,然后对象根据这个模板生成。
经典的面向对象编程语言(比如java和C++),存在“类”(class)这个概念。所谓“类”就是对象的模板。它提供模板,描述对象的基本结构。一个构造函数可以生成多个对象,这些对象都具有相同的结构。
构造函数的写法就是一个普通函数,但是有自己的特征和用法
var Vehicle = function(){
this.price = 100;
};
// 函数三种创建方法:function fun_name(){}; var = function(){}; new function();
上面代码中,Vehicle就是一个构造函数,它提供模板,用来生成实例对象,为了与普通函数区别,构造函数名字的第一个字母通常大写。
特征: 函数体内部使用了this关键字,代表所要生成对象实例.
用法: 生成对象的时候,必须用new命令,调用Vehicle函数.
三、new命令
- 基本用法
new命令的作用,就是用来执行构造函数,返回一个实例对象。
var Vehicle = function(){
this.price = 100;
};
var v = new Vehicle();
v.price //100
上面代码通过new命令,让构造函数Vehicle生成一个实例对象,保存在变量v中,这个新生成的实例对象,从构造函数vehicle中继承了price属性,new命令执行时,构造函数内部的this,就、代表了新生成的实例对象,this.price表示实例对象有一个price属性,值是100.
使用new命令时,根据需要,构造函数也可以接受参数
var Vehicle = function(p){
this.price = p;
};
var v = new Vehicle(300);
new命令本身可以执行构造函数,所以后面的构造函数可以带括号也可以不带括号.
var v = new Vehicle();
var v = new Vehicle;
以上代码是等价的。
一个问题,如果你忘记使用new命令,直接调用构造函数会发生什么?
这种情况下,构造函数就变成普通函数,并不会生成实例对象,而且由于后面说的原因,this这个时候代表全局对象。造成一些意想不到的结果。
var Vehicle = function(){
this.price = 100; //this指向全局对象,浏览器下是window。
};
var v = Vehicle();
v.price //报错
price //100
上面的代码中,调用vehicle构造函数,忘了加上new命令,结果,price属性变成了全局变量,而变量v的值变成undefined。(因为Vehicle没有返回值)
因此,应该非常小心,避免出现不使用new命令,直接调用构造函数的情况,为了保证构造函数必须和new命令一起使用。一个解决办法是,在构造函数的内部使用严格模式,就是在第一行加上“use strict”。(严格模式下JavaScript不允许对undefined添加属性)
function Fubar(foo,bar){
'use strict';
this._foo=foo;
this._bar=bar;
}
Fubar();
上面代码Fubar为构造函数。use strict命令保证了该函数在严格模式下运行,由于严格模式中,函数内部的this不能指向全局对象,默认等于undefined,导致不加new调用时会报错(严格模式下,Javascript不允许对undefined添加属性)
另外一种解决方法,是在构造函数内部判断是否使用new、命令,如果发现没有new,则返回一个实例对象
function Fubar(foo,bar){
if(!(this instanceof Fubar)){
return new Fubar(foo,bar);
}
this._foo = foo;
this._bar = bar;
}
Fubar(1,2).foo
(new Fubar(1,2)).foo
在上面代码中的构造函数,不管加不加new命令,都可以获得一个新的对象。
- new命令的原理
使用new命令时,他后面的函数调用就不是正常调用,而是依次执行下面的步骤
- 创建一个空对象,作为要返回的实例对象
- 将这个对象的原型,指向构造函数的prototype属性
- 将这个空对象赋值给函数内部的this关键字
- 开始执行构造函数内部的代码
也就是说构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(this对象),将其“构造”为所需要的样子。
如果构造函数内有return语句,而且return后面跟着一个对象,new命令将会返回return语句指定的对象:否则不管return语句,返回this对象。
但是,如果return语句返回的是一个跟this无关的对象,new命令会返回这个新对象,而不是this对象。
如果普通函数(函数内部没有this关键字)使用new命令,则会返回一个空对象。
new命令简化的内部流程, 下面的代码表示
function _new(constructor/*构造函数*/,parmal/*构造函数参数*/){
var args = [].slice.call(arguements);
//将argumenets转换成数组
var constructor = args.shift();
//取出构造函数
var context = Object.create(constructor.prototype);
// 创建一个空对象,继承构造函数的prototype属性
var result = constructor.apply(context,args);
// 返回结果如果是对象,就直接返回,否则返回context对象
return (typeof result === "object"&& result != null) ? result : context ;
}
- new.target
函数内部可以使用new.target属性。如果,当前函数是new命令调用的,new.target指向当前函数,否则指向undefined。
function fn(){
console.log(new.target === fn);
}
function fn(a){
if(!new.target){
throw new Error('请使用new操作符调用!');
}
this.a = a;
}