一、面向对象
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); // undefinedconsole.log(f1 instanceof Fn); // truelet 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:windowobj.fn(10, 20); // 报错:obj 中没有 fn 属性document.body.onclick = fn; // 点击 fn 后的 this:BODYdocument.body.onclick = function () {// this:BODYfn(30,40); // this:window};fn.call(); // this:window 不传或者传递 null / undefined 都是 windowfn.call(obj,10,30); // this:objfn.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:fFn.prototype.sum(); // this:fn.prototypef.__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:windowobj.fn.call(obj); // this:window//==================document.body.onclick=function(){// this:BODYlet _this=this;_this.count=0;/*setTimeout(function(){// this:window_this.count++;},1000);*/setTimeout(()=>{// this:没有自己的 THIS,继承下上文中的,也就是 BODYthis.count++;},1000);}
