面向对象基础
面向对象:object oriented programming OOP编程/设计思想
+ JS
+ JAVA
+ Python
+ PHP
+ C#
+ C++
+ GO
+ ...
面向过程:procedure oriented programming POP
+ C语言
面向对象是基于类和实例来管理一门语言的;
@1 在JS中所有要学习和研究的内容都可以被称为“对象”、JS把其划分为很多类、我们平时就是创造这些类的实例进行开发的;
@2 每个实例之间有一些自己的私有属性和方法,也具备类赋予他们的公共的属性和方法;
@3 JS语言本身就是基于 “面向对象” 这种思想设计的语言,所以我们学习和开发也应该按照这种思想来进行处理!!
构造函数执行的机制
JS中的内置类
@1 数据类型所属内置类
+ Number 数字类 每一个数字都是他的一个实例「含NaN」
+ String
+ Boolean
+ Symbol
+ BigInt
+ Object
+ Function
+ Array
+ RegExp
+ Date
+ Error
+ …
@2 DOM元素对象也有自己所属的内置类
+ 每一种HTML标签都有自己所属的内置类 HTMLDivElement / HTMLAnchorElement … -> HTMLElement -> Element -> Node -> EventTarget -> Object
+ document实例 -> HTMLDocument「和 XMLDocument」 -> Document -> Node …
+ window实例 -> Window -> WindowProperties -> EventTarget -> Object
+ 元素集合 -> HTMLCollection -> Object
+ 节点集合 -> NodeList -> Object
示例:
const Fn = function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
let res1 = Fn(10, 20); //普通函数执行
console.log(res1); //undefined
console.log(window); //window.total、window.say
let f1 = new Fn(10, 20); //构造函数执行「面向对象思维处理:创造类和实例 基于NEW执行,Fn是类,f1是实例」
console.log(f1);
let f2 = new Fn(100, 200);
console.log(f2);
console.log(f1.say === f2.say); //false 构造函数体中 this.xxx=xxx 给实例设置的私有属性方法
new执行(构造函数执行)也会和普通函数一样创建一个私有上下文(相同点)
区别:
1、会创建当前类、构造函数的一个空的实例对象
2、让构造函数中的this指向这个实例对象
3、代码执行完,观察函数的返回值
- 如果return的一个对象则以自己返回的为主
- 如果返回的是一个原始值或者是没有返回return则默认返回的是这个实例对象
二、「原型和原型链」
大部分“函数数据类型”的值都具备“prototype(原型/显式原型)”属性,属性值本身是一个对象「浏览器会默认为其开辟一个堆内存,用来存储实例可调用的公共的属性和方法」,在浏览器默认开辟的这个堆内存中「原型对象」有一个默认的属性“constructor(构造函数/构造器)”,属性值是当前函数/类本身!!
1、函数数据类型
- 普通函数(实名或者匿名函数)
- 箭头函数
- 构造函数、类(内置类、自定义类)
- 生成器函数
2、不具备prototype的函数
- 箭头函数
- 基于es6给某个成员赋值函数值得快捷键操作
每一个“对象数据类型”的值都具备一个属性“proto(原型链/隐式原型)”,属性值指向“自己所属类的原型prototype”
3、对象数据类型值
- 普通对象
- 特殊对象:数组、正则、日期、Math、Error…
- 函数对象
- 实例对象
- 构造函数.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(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX();
f1.__proto__.getX();
f2.getY();
Fn.prototype.getY();
例如:
function C1(name) {
if (name) {
this.name = name;
}
}
function C2(name) {
this.name = name;
}
function C3(name) {
this.name = name || 'join';
}
C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';
alert((new C1().name) + (new C2().name) + (new C3().name));
最终结果:
'Tom' + undefined + 'join' => "Tomundefinedjoin"
三、基于内置类原型扩展方法
内置类原型指向图:arr -> Array.prototype -> Object.prototype
数组去重:
// 基于内置类原型扩展方法「实例可以直接调用」:设置的名字不要和内置的方法名冲突{一般自己会设置前缀 myXxx}
Array.prototype.unique = function unique() {
// this -> 我们要处理的数组
return Array.from(new Set(this));
};
let arr = [1, 3, 2, 4, 3, 2, 2, 3, 4, 2, 5, 6, 3, 2, 1, 4, 5];
arr = arr.unique().sort((a, b) => a - b);
console.log(arr);
调用所属类原型上的内置属性方法 实例.方法()
+ 方便
+ 实现链式写法「执行完一个方法,返回的结果还是当前类的实例,这样就可以继续调用,类为其提供的其它方法执行」
arr.sort((a, b) => a - b).map(item => item * 10).filter(item => item < 50).push('zhufeng');
四、基于内置类原型扩展方法——面试真题
1、装箱
装箱的操作:浏览器首先会把原始值变为对象类型的实例「new Number(10) 或者 Object(10)」,然后再继续调用toFixed等方法…
10 是个数字,它本身是不具备键值对的,但是它是Number类的实例,可以调用Number.prototype上的方法
2、拆箱
把对象类型的实例变为原始值类型的值{Number(对象)} Symbol.toPrimitive -> valueOf -> toString -> 变为数字
3、真题
真题描述: let n = 10; let m = n.plus(10).minus(5); console.log(m);
//=>15(10+10-5)
(function (proto) {
// 检测num是否是合法数字
const checkNum = num => {
num = +num;
return isNaN(num) ? 0 : num;
};
proto.plus = function plus(num) {
num = checkNum(num);
return this + num;
};
proto.minus = function minus(num) {
num = checkNum(num);
return this - num;
};
})(Number.prototype);
五、私有和公有属性的检测
1、Object.prototype.hasOwnProperty 私有检测是自己的私有属性
2、in 私有检测是自己的属性(公有 私有)
3、自己重构:Object.prototype.hasPubProperty
思路一:是它的属性,但是还不是私有的,这样只能是公有的属性
Object.prototype.hasPubProperty = function hasPubProperty(attr) {
return (attr in this) && !this.hasOwnProperty(attr);
};
但是会有一个问题
let arr = [10, 20, 30];
arr.push = ‘push’;
console.log(arr.hasPubProperty(‘push’)); //false
例如这种情况push即是私有属性有事共有属性的时候返回的是fasle 按理说应该是true所以这个时候就出现了bug
所以这种思路是不靠谱的
思路二:跳过私有的查找「无论私有有没有,都无所谓」,直接找公共的「所属类的原型 先找自己所属类的原型,没有基于__proto__再继续向上找,直到Object.prototype为止;如果中间找到返回true,找到头都没有就是false!」
Object.prototype.hasPubProperty = function hasPubProperty(attr) {
let self = this,
prototype = Object.getPrototypeOf(self);
while (prototype) {
if (prototype.hasOwnProperty(attr)) {
return true;
}
prototype = Object.getPrototypeOf(prototype);
}
return false;
这样才能满足上面的要求,完美的方案!