目录
面向对象编程OOP(Object Oriented Programming)
面向对象编程OOP(Object Oriented Programming)
定义:
- 对象: 万物皆对象
- 类: 对对象的细分
- 实例: 类中的具体事物
在JavaScript中, 对实例、类和对象的划分如下:
实例 | 类 | 对象 |
1 | Number | Object |
"1" | String | Object |
[1,2,3] | Array | Object |
ture、false | Boolean | Object |
null | Null | Object |
Undefined | Undefined | Object |
function() | Function | Object |
Date | Date | Object |
{name:"1"} | Object | Object |
/^$/ | RegExp | Object |
上面这些类都是JavaScript自身所拥有的, 那么应该如何创建一个自定义类呢?创建自定义类的过程中都发生了什么?
自定义类
创建自定义类
function func() {
this.num = 100;
}
func(); //此情况为普通函数执行, this指向window
new func(); //此情况new执行, 就是一个自定义类
new函数执行的过程(加粗文字是与普通函数执行不同的地方)
- 形成一个全新的执行上下文(EC) [每一次new都会形成一个新的实例对象]
- 形成AO变量对象
- 初始化作用域链
- 默认创建一个对象, 这个对象就是当前类的实例
- 声明this指向新创建的实例
- 代码执行
- 不论是否有return, 都会将新创建的实例返回
- 如果有return, 且返回值是一个引用类型值, 就会返回return的值, 如果不是引用类型值, 就会返回创建的实例
- 如果没有return, 就会返回创建的实例
// 1.无return
function func() {
this.num = 100;
}
let f = new func();
console.log(f); //func {num: 100}
// 2.有return, 且返回值为引用类型值
function func1() {
let obj = {};
obj.num = 10;
this.num = 100;
return obj;
}
let f1 = new func1();
console.log(f1); // {num:10}
// 3.有return, 但是返回值不是引用类型值
function func2() {
this.num = 100;
return 1;
}
let f2 = new func2();
console.log(f2); //func2 {num: 100}
其实在上面的例子中, 返回的实例里面并不是只有num一个键值对, 我们在浏览器中的输出展开实例会发现, 其中还有一个__proto__
下面说一下prototype和__proto__
原型、原型链的底层运行机制
原型和原型链就是对应上面所说的prototype和__proto__, 在此之前我们先了解一下作用链
- 每一个类都具备prototype, 并且属性值是一个对象
- 对象上天生具备一个属性: constructor, 指向类本身
- 每一个对象(普通对象、prototype、实例...)都具备__proto__, 属性值是当前实例所属类的原型
- __proto__的机制: 先找私有属性, 如果没有则开始找基于 __proto__所属实例prototype上的公有属性, 如果还是没有, 则继续向上查找, 一直到找到 Object.prototype
接下来, 看一道面试题, 我们还是用绘图的方式进行理解:
function Fn() {
this.x = 100;
this.y = 200;
this.getX = function () {
console.log(this.x);
}
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.prototype.getY = function () {
console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);
console.log(f1.getY === f2.getY);
console.log(f1._proto_.getY === Fn.prototype.getY);
console.log(f1._proto_.getX === f2.getX);
console.log(Fn.prototype.getX === f1.getX);
console.log(f1.constructor);
console.log(Fn.prototype._proto_.constructor);
f1.getX();
f1._proto_.getX();
f2.getY();
f2.prototype.getY();
上面的图中, 展示了这道题中的几个关系, 其中第六条说, 每一个__proto__都会指向当前实例所属类的原型, 那么Fn和Fn.prototype的__proto__指向了哪里?我们知道, 函数所属的类是Object, 那它们两个就是指向了Object, 下面是示意图:
依据上面两张图, 我们就可以知道上面的答案了
console.log(f1.getX === f2.getX); // false (每new一次, 都创建一个新的实例对象)
console.log(f1.getY === f2.getY); // true (两个实例都是根据Fn创建的, 他们共享相同的getY函数)
console.log(f1._proto_.getY === Fn.prototype.getY); // true (f1.__proto__ 指向了 Fn.prototype,所以他们指向的getY函数是同一个)
console.log(f1._proto_.getX === f2.getX); // ture (f2 的 getX 方法是通过 Fn.prototype 继承而来的,所以它们是同一个函数。)
console.log(Fn.prototype.getX === f1.getX); // false (f1.getX 是在构造函数内部定义的,它不是通过 Fn.prototype 继承而来的)
console.log(f1.constructor); // [Function: Fn]
console.log(Fn.prototype._proto_.constructor); // [Function: Object]
f1.getX(); // 100
f1._proto_.getX(); // undefined (f1.__proto__ 中并没有定义 x 属性)
f2.getY(); // 200
f2.prototype.getY(); // undefined (f2 的实例对象没有 prototype 属性)