一、面向对象
1. oop面向对象编程
在 js 中面向类 / 实例进行程序设计,就是经典的面向对象编程
- 类和实例的创建(构造函数模式)
- prototype / proto ( 原型和原型链)
- 函数的三种角色
- 基于内置类原型拓展方法,实现链式写法
- 借用内置类原型上的方法,实现一些特殊的需求(例如:把类数组转换为数组)
- 细小的知识点:instanceof / constructor / hasOwnProperty…
- 类的继承封装和多态
- ……
2. 构造函数
fucntion fn (n,m) {
let total = n + m,
minus = n - m;
this.x = plus;
this.y = minus;
this.print = function () {
console.log(this.x + this.y);
}
}
// 普通函数执行
fn(10,20);
// 1. 形成私有的栈内存(私有作用域 scope)
// 2. 形参赋值 & 变量提升 n = 10 m = 20
// 3. 代码执行 this => window
// 4. 没有 return 返回值
// 构造函数执行
let f1 = new Fn(30,10);
fi.print();
console.log(f1.plus); // undefined
console.log(f1 instanceof Fn); // true
let f2 = new Fn; // 没有传递任何实参
console.log(f1.print === f2.print)
// 1. 开辟一个新的私有作用域
// 2. 形参赋值 & 变量提升
// 3. 浏览器在当前作用域中创建一个实例对象 @a,并且让 this 指向它
// 4. 代码执行 this => 当前类的实例 @a this.xxx = xxx; 都是给当前实例 @a 设置的私有属性,除此之外的私有变量等和 @a 这个实例没有必然的关系
// 5. 即使我们不设置 return,浏览器也会默认把实例返回,而外面的 f1 / f2 接收的就是返回的实例,所以也说 f1 / f2 是 Fn 这个类的实例,(如果手动返回的是引用数据类型值,会以用户返回的为主,也就是返回的不再是 Fn 的实例,如果返回基本类型值,对原有的操作无影响
3. 原型:prototype 和原型链:proto
- 每一个函数数据类型都自带一个属性:prototype,他的属性值是一个对象
- prototype这个对象中有一个默认的属性:construct,存储函数本身
- 每一个对象都自带一个属性:proto,它的属性值是所属类的原型
- 普通对象、数组、正则、日期、都是对象
- 类的实例时对象:基本数据类型值虽然是所属类实例,但不是对象
- prototype 原型属性值也是对象
- 函数本身也是一个对象
- JavaScript 万物皆对象
二、函数三种角色
普通函数:闭包作用域、作用域链 构造函数:面向对象、原型链 普通对象:键值对
三、基于内置类原型扩展方法,实现链式调用
let ary = [12, 13, 13, 12, 24, 12, 12];
// ary 是 Array 数组类的实例,所以可以调取 Array.prototyoe 上的方法,sort 方法中的 this 是 ary,当前要排序的数组实例(底层理解:sort 是内置方法,它可以实现排序,ary.sort(...)本意:ary 先基于 __proto__ 找到 Array.prototype 上的 sort 方法,并且把 sont 方法执行,方法中的 this 是 ary,sort 方法在执行时,会把 this 对应的数组进行排序处理)
ary.sont((a, b) => a - b)
// slice 执行的时候,方法中的 this 是谁,就相当于把谁克隆成一份全新的数组出来
Array.prototype.slice = function () {
let newAry = [];
for(let i = 0; i < this.length; i++) {
newAry.push(this[i]);
}
rerutn newAry;
}
let newAry = ary.slice(0);
newAry = Array.prototype.slice.call(ary,0); // 等价的
// 类数组转数组
fucntion fn () {
// arguments 类数组集合(实参集合):不是 Array 的实例,它就是一个对象而已,不能直接使用数组中的方法 =》 把类数组转换为数组
// 两种方式
let ary = Array.prototype.slice.call(arguments,0);
ary = [].slice.call(arguments,0);
// 借用数组原型上的 forEach 方法,实现给类数组进行循环(内置方法中的 this 是谁,其实当前方法就在操作谁)
[].forEach.call(arguments,item => {});
}
fn(10,20, 30, 40, 50);
内置方法很多,但是不一定完全够用,很多时候我们需要自己向内置类的原型上拓展方法来实现一些需求
~function () {
function unique () {
// 注意:this 是谁就给谁去重
let temp = {};
for (let i = 0; i < this.length; i++) {
let item = this[i];
if(typeof temp[item]! == "undefined") {
this[i] = this[this.length-1];
this.length--;
i--;
continue;
}
temp[item] = item;
}
temp = mull;
// 注意:返回的结果如果还是数组,则基础可以调取 Array.prototype 上的方法,实现链式调用
return this;
}
// 往内置类原型上扩展方法,为了防止不修改原有内置的方法,我们扩展的方法名要增加对应的前缀
Array.prototype.myUnique = unique;
}();
let ary = [12, 13, 13, 12, 24, 12, 12];
ary.myUnique();
array.prototype.myUnique.call(ary);
四、this问题
this 函数执行的主体:谁执行的 this 是谁和函数在哪执行和在哪定义都没关系,想要分清执行主体记住一下规律即可
- 给元素的某个事件绑定方法,当事件触发方法执行的时候,方法中的 this 时当前元素本身
- 方法执行,看方法名前面是否有点,有点,点前面是谁,this 就是谁;没有点 this 就是 window(js 在严格模式下,没有点 this 是 undefined)
“use strict” 开启严格模式
- 自执行函数,回调函数等方法中的 this 一般是 window
- 在构造函数执行过程中,构造函数体中的 this 是当前类的实例
- 使用 CALL / APPLY / BIND 可以改变函数中的 this 指向
- ES6 箭头函数中没有自己的this,所用的 this 是继承上下文中的
function fn (n,m) {
this.total = n + m;
}
let obj = {name:'OBJ'};
fn(10,20); // this:window
obj.fn(10, 20); // 报错:obj 中没有 fn 属性
document.body.onclick = fn; // 点击 fn 后的 this:BODY
document.body.onclick = function () {
// this:BODY
fn(30,40); // this:window
};
fn.call(); // this:window 不传或者传递 null / undefined 都是 window
fn.call(obj,10,30); // this:obj
fn.apply(obj,[10, 30]); // this:obj apply 要求传递的参数是数组
document.body.onclick = fn.bind(obj,10,30); // bind 是预先处理 this,此时的 fn 还没有执行,只是把 this 改成了 obj,点击 body 的时候才执行的 =》柯里化函数(预处理机制)
1. 构造函数中的 this
function Fn () {
this.x = 100;
this.y = 200;
};
fn.prototype.sum = funciton () {
console.log(this.x + this.y);
};
let f = new Fn; // Fn 中的 this:f 当前类的实例
f.sun(); // this:f
Fn.prototype.sum(); // this:fn.prototype
f.__proto__sum; // this:f.__proto__
es6的写法
class Fn{
constructor () {
// this:当前 Fn 的实例
this.x = 100;
this.y = 200;
}
// 直接写的方法就是放到原型上
sum() {
console.log(this.x + this.y);
}
// static 修饰的都是把 Fn 当作普通对象设置的键值对
static unique(){}
}
Fn.prototype.name = '珠峰';
Fn.age = 10;
let f = new Fn;
f.sum();
Fn.unique();
2. 箭头函数中 this
window.name='WINDOW';
let obj={
name:'OBJ',
fn:()=>{
console.log(this.name);
}
};
obj.fn(); // this:window
obj.fn.call(obj); // this:window
//==================
document.body.onclick=function(){
// this:BODY
let _this=this;
_this.count=0;
/*setTimeout(function(){
// this:window
_this.count++;
},1000);*/
setTimeout(()=>{
// this:没有自己的 THIS,继承下上文中的,也就是 BODY
this.count++;
},1000);
}