面向对象、面向过程和构造函数
面向对象:把数据和对数据的操作方法放在一起,构成一个相互依存的整体,对象和对象之间可以通信,程序流程由用户在使用中确定。
面向过程:自顶向下执行,程序结构按照功能划分成各个基本的模块,程序的流程在写程序时已经确定。
在js中创建对象有两种办法,对象字面量和使用new表达式,在使用new表达式创建对象时就使用到了构造函数。
一个函数作为构造函数和普通函数返回值是不同的。
- 当return的内容是引用类型时
function Test(){
this.a = 10;
return function(){
return 1;
}
// return 1;
}
var b = Test();
console.log(b);
console.log(typeof(b));
var c = new Test();
console.log(c);
console.log(typeof(c));
运行结果如下
c作为Test构造函数的实例,this.a丢失,被返回值所取代。
- 当return的内容是值或者没有return时
function Test(){
this.a = 10;
// return function(){
// return 1;
// }
return 1;
}
var b = Test();
console.log(b);
console.log(typeof(b));
var c = new Test();
console.log(c);
console.log(typeof(c));
运行结果如下
这时构造函数成功的构造了c实例。
new关键字做了什么
- 在构造函数内创建了一个临时的对象
- 将构造函数内临时对象的__proto__属性和构造函数的原型进行绑定
- 将临时对象的作用域赋给真正的对象即改变this指针的指向,并将创建好的对象返回
- 将所有构造函数的原型统一命名为prototype
原型和原型链
我个人觉得最绕的地方,首先名词多,然后互相之间的关系歪七扭八的。接下来我用我能理解的方法解释下这个问题。
这个问题的产生是为了实现原型上属性和方法的继承,提高代码的复用率。构造函数就相当于模具,但是关于这个模具具体的一些属性,比如大小,材质之类的并没有直接写在模具里,而是把这些属性和方法放在了原型(prototype)里,在构造函数内部通过__proto__属性将两者绑定了。在js万物皆对象的思想下,__proto__属性是每个对象都有的,包括原型。
ps:因为绝大部分浏览器都支持__proto__属性,所以它才被加入了 ES6 里(ES5 部分浏览器也支持,但还不是标准)。
然后在有了模具的前提下,就可以来创造实例了,但是实例不光可以继承属性和方法,还可以有自己的属性和方法。就像父子的关系一样,虽然相似但也有不同。而原型中也有一个属性指向创造出来的实例,就是构造器(constructor)属性,构造器(constructor,包括: Object;Function;Array;Date;String等函数)才有prototype属性,对象(除Object外)都拥有__proto__。
所以在通过构造函数创造实例的过程中,构造函数只是一个桥梁,将实例通过原型创造出来了而已,在实例被创造出来的时候,构造函数中的this指针就已经指向被创造的实例,实例的__proto__就指向了实例的原型,而构造函数就退出历史舞台。而原型链指的是,在找寻某个实例的方法和属性的过程中,先在自身查找,然后通过实例的__proto__找到实例的原型,实例的原型再通过实例的原型的__proto__找到了Object的原型,Object的原型再通过Object的原型的__proto__找到Object的原型的__proto__的原型为null,就到顶了。所以说原型链是通过__proto__而不是原型,而且这个连接存在在实例和原型之间,和构造函数没有什么关系。
以下是引用
所有函数对象proto都指向 Function.prototype,它是一个空函数(Empty function)。
1、Object.proto === Function.prototype // true
Object 是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype。
2、Function.proto === Function.prototype // true
Function 也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype
3、Function.prototype.proto === Object.prototype //true
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.proto === null,保证原型链能够正常结束。