JavaScript 面向对象编程
面向对象编程——OOP(Object Oriented Programming)是一种程序设计思想,把对象作为程序的基本单元,对业务功能进行抽象和封装
构造函数 Constructor
构造函数是生成对象的模版,在 Java 等面向对象的编程语言中存在类(Class)的概念,用来编写对象的模版,但是 ES5 规范里并没有类的概念,而是基于构造函数和原型链来实现类和继承
构造函数的原理
ES5 并没有类和构造函数的语法,当一个普通的函数遇上关键字new,那神奇的事情就发生了,使用new关键字执行函数会触发构造运行。通过new关键字就使一个普通的函数变成了生成对象的模版,可以在函数内部通过this关键字在生成的对象实例上生成属性和方法,内部流程如下:
- 首先
new关键字会创建一个对象 - 然后将对象的原型(
__proto__)指向构造函数的prototype对象 - 将函数的
this就指向这个对象,执行函数,这时对this的赋值都会作用在创建的对象上 - 函数执行完成,判断返回数据类型
- 如果函数未声明返回值,或者声明的返回值为基本类型,那么返回值为
this。 - 如果返回类型为引用类型,那么就返回这个对象,这样就失去了构造函数的本意。
- 如果函数未声明返回值,或者声明的返回值为基本类型,那么返回值为
function objectFactory() {
// 创建对象
const obj = new Object();
const args = Array.prototype.slice.call(arguments);
// 获取构造函数,设置原型链
const Constructor = args.shift();
obj.__proto__ = Constructor.prototype;
// 调用构造函数为对象赋值
const result = Constructor.apply(obj, args);
// 返回对象
return typeof result === "object" ? result : obj;
}
function A(x,y){
this.x = x;
this.y = y;
this.toString = function(){
return this.x + this.y;
}
}
function B(x,y){
this.x = x;
this.y = y;
this.toString = function(){
return this.x + this.y;
}
return "Constructor return base Type";
}
function C(x,y){
this.x = x;
this.y = y;
this.toString = function(){
return this.x + this.y;
}
return {x:x,y:y};
}
A(1,2);// undefined;
var a = new A(1,2);
a.toString();// 3;
B(1,2);// "Constructor return base Type"
var b = new B(1,2);
b.toString();// 3;
C(1,2); // Object {x: 1, y: 2}
var c = new C(1,2);
c.toString();// "[object Object]"
在上面的例子中方法 A 和方法 B 中由于没有声明返回类型或者声明的返回类型为基本类型,所以声明了也白声明,返回的为新开辟的内存空间,即this所指向的部分;
在两个方法中均对toSting()方法进行覆盖,所以通过方法 A 和方法 B 生成的实例调用toString方法时指向的是我们自己声明的方法;
在方法 C 中因为返回类型是一个对象,那么生成的实例即为这个对象,覆盖的toString方法并没有生效,调用的仍然是继承自 Object 的toString方法;
由此可见我们声明的构造函数如果不通过new关键字调用那么就不是我们想要的效果,我可以通过以下两种方式处理这个问题:
一:使用严格模式,没有使用new关键字,严格模式下函数内部this不能指向全局对象,默认等于undefined,对undefined进制操作会报错
function A(x){
'use strict';
this.x = x;
}
A(1); //Uncaught TypeError: Cannot set property 'x' of undefined(…)
二:使用new.target属性;
new.target可以检查函数或者构造方法是否是通过new运算符调用,是的话会返回指向构造方法或函数的引用,普通执行的函数返回值是undefined
function Foo() {
if (!new.target) throw "Foo() must be called with new";
console.log("Foo instantiated with new");
}