Javascript基础进阶:面向对象和原型原型链

开发 前端
Javascript就是基于“面向对象”思想设计的编程语言,本篇给大家介绍Javascript基础进阶- 面向对象和原型原型链

[[347261]]

 * 学习进阶方式 💪

基础知识要夯实;

原理源码要深入;

深度广度要扩展;

简短的概括:

1、面向对象理论知识总述

2、自定义类的创建和一些细节知识

3、面向对象中的原型和原型链

4、重写内置new以及基于内置类原型扩展方法

5、THIS情况汇总及CALL、APPLY、BIND的应用

面向对象理论知识总述

* 核心答案 | 基础知识要夯实

编程语言

1、OOP面向对象:

1)java,

2)python,

3)C++,

4)php,

5)C#(ASP.NET),

6)javascript -> Node.js

2、POP面向过程:

HTML和CSS是标记语言

1)less/sass/stylus:CSS预编译语言,让CSS具备面向对象编程的特点。

2)写完的代码无法被浏览器直接识别,需要编译后(编译成为正常的CSS)在浏览器中渲染。

什么是面向对象编程?

1、对象:泛指,万物皆对象(JS中所有我们学习研究和开发的都是对象 「研究对象」);

2、类:对 “对象” 的一个细分,按照对应的功能特点,分成我们的大类和小类「类别」;

3、实例:某个类别中具体的事物;

* 关于类的“封装、继承、多态”

1)封装:把实现某个功能的代码封装到函数中,起到“低耦合高内聚”的作用

2)继承:子类及子类的实例继承了父类中的属性和方法

3)多态:函数的重载(方法名字相同,但是传递参数的个数或者类型不同,识别为两个不同的方法 -> 后台语言有这个特征,但是JS中不存在严格意义上的重载)和重写(子类重写父类的方法)

JS就是基于“面向对象”思想设计的编程语言

1、本身存在很多“内置类”

1)每一个数据类型都有一个自己所属的内置类

2)获取的元素集合或者节点集合也是有自己的类 HTMLCollection / NodeList

3)每一个元素标签都有自己所属的类

2、我们学习JS:拿出某个类的一个实例去研究和学习,当前实例研究明白后,那么当前实例所属类下的其他实例,也具备这些特点... 

自定义类的创建和一些细节知识 

* 核心答案 | 基础知识要夯实

自定义类(所有的类「内置类/自定义类」都是“函数数据类型”的值)

函数执行的时候基于new执行即可 “构造函数执行”。

🌰 例如一:普通函数 与 构造函数

  1. function Fn(x, y) { 
  2.     let total = x + y; 
  3.     this.x = x; 
  4.     this.y = y; 
  5.     return total; 
  6. // 1、作为普通函数执行 
  7. Fn(10, 20); 
  8.  
  9. // 2、构造函数执行 
  10. // 说明:f1是Fn这个类的一个实例对象 
  11. let f1 = new Fn(10, 20); 
  12. console.log(f1.x, f1.y, f1.total);  
  13. // 说明:total只是上下文中的私有变量,和实例f1没有关系」 
  14. // 结果:10 20 undefined 

画图分析:( 有图有真相 )

构造函数 VS 普通函数

1、构造函数执行,最开始会像普通函数执行一样,形成私有的上下文。

1)AO;

2) SCOPE-CHAIN;

3)形参赋值;

4)变量提升;

5)代码执行;

不同的地方:

1、创建上下文之后,浏览器默认帮助我们创建一个对象 “实例对象”。

1)把当前Fn函数当作一个类“构造函数”

2)创建的对象就是这个类的一个实例

2、初始this的时候,让this指向当前创建的实例对象。

3、在代码执行完,返回值的时候

1)如果函数没有写return,或者返回的是一个基本数据类型值,则浏览器默认,会把创建的实例对象返回;

2)如果函数本身返回的就是一个引用数据类型值,还是以自己返回的为主。

🌰 例如二:构造函数扩展

  1. function Fn(x, y) { 
  2.     let total = x + y; 
  3.     this.x = x; 
  4.     this.y = y; 
  5.     return { 
  6.         name'前端学苑' 
  7.     }; 

说明:

由于构造函数体中,默认自己返回一个引用类型值,所以f1不再是创建的Fn实例,而是自己返回的对象。

let f1 = new Fn(10, 20);

“实例 instanceof 构造函数” :检测当前实例是否属于这个类。

console.log(f1 instanceof Fn); //false

🌰 例如三:函数扩展

  1. function Fn(x, y) { 
  2.     let total = x + y; 
  3.     this.x = x; 
  4.     this.y = y; 
  5.     this.say = function say() { 
  6.         console.log(`SAY:${total}`); 
  7.     }; 
  8. let f1 = new Fn(10, 20); 
  9. let f2 = new Fn; 

画图分析:( 有图有真相 )

结果:

  1. console.log(f1 === f2); //false 
  2. console.log(f1.say === f2.say); //false 

Fn VS Fn()

1) Fn代表的是函数本身(堆内存 -> ƒ Fn(x, y) {...});

2) Fn()是把函数执行,获取其返回值; 

new Fn VS new Fn()

都是把Fn执行了,只是第一个没有传递实参,第二个可以传递实参而已。

1) new Fn; 运算优先级是18(无参数列表new)

2) new Fn(); 运算符优先级是19(有参数列表new)

* 检测一个属性是否为当前对象的成员

1)属性名 in 对象:不论是私有属性还是公有的属性,只要有就是true;

2)对象.hasOwnProperty(属性名):必须是对象的私有属性,结果才是true;

说明:自己扩展一个方法 hasPubProperty(对象,属性名):检测当前属性是否属于对象的公有属性(特点:必须有这个属性,而且不是私有的)(需要扩展)

属性和变量有什么共同点:

没有。属性是堆内存的成员,变量是栈内存或者上下文当中变量。

  1. console.log('say' in f1); //true 
  2. console.log('toString' in f1); //true 
  3. console.log('total' in f1); //false 
  4. console.log(f1.hasOwnProperty('say')); //true 
  5. console.log(f1.hasOwnProperty('toString')); //false 
  6. console.log(f1.hasOwnProperty('total')); //false 

🌰 例如四:函数扩展

  1. Object.prototype.AA = '前端学苑'
  2. let obj = { 
  3.     name'xxx'
  4.     age: 11, 
  5.     0: 100, 
  6.     [Symbol('AA')]: 200, 
  7.     [Symbol.toPrimitive]: function () { 
  8.         return 0; 
  9.     } 
  10. }; 

基于“for...in”循环遍历对象

1) 优先遍历数字属性;

2) 不会遍历到Symbol属性;

3) 会把自己扩展到“类原型”上的公共属性方法也遍历到「可枚举的」

  1. for (let key in obj) { 
  2.     // 在遍历过程中,遍历到公共属性,则停止遍历: 
  3.     // 因为for...in遍历的本意就是只遍历私有的属性即可 
  4.     if (!obj.hasOwnProperty(key)) break; 
  5.     console.log(key); 

  1. let keys = [ 
  2.     ...Object.keys(obj), 
  3.     ...Object.getOwnPropertySymbols(obj) 
  4. ]; 
  5. keys.forEach(key => { 
  6.     console.log(`属性名:${String(key)},属性值:${obj[key]}`); 
  7. });  

说明:

1)Object.keys(obj):获取当前对象所有非Symbol的私有属性「数组」 =>Object.getOwnPropertyNames. 

2)Object.getOwnPropertySymbols(obj):获取对象所有的Symbol私有属性「数组」.

面向对象中的原型和原型链 

* 核心答案 | 基础知识要夯实

1、函数数据类型

1)普通函数

2)箭头函数

3)生成器函数

4)构造函数(类)

2、对象数据类型

1)普通对象/数组对象/正则对象/日期对象...

2)实例也是对象数据类型的(排除7种原始值类型)

3)prototype/__proto__原型属性值也是对象(排除Function.prototype)

3、大部分函数(重点是构造函数) 都内置一个prototype(原型「显式原型」)的属性,属性值是一个对象,对象中存储的属性和方法,是供当前类所属实例,调用的“公共”的属性和方法

1)箭头函数是没有prototype属性的;

2)在原型对象上有一个内置的属性 constructor(构造器),属性值是当前函数本身;

4、每一个对象都内置一个__proto__(原型链「隐式原型」)的属性,属性值指向自己所属类的原型prototype对象。

1)Object.prototype这个对象的__proto__值是null,因为Object是所有对象的“基类”

只绘制“堆内存”,画图分析:( 有图有真相 )

解析说明:

每一个数组都是Array类的实例,所以每一个数组的_proto_一定指向Array.prototype; 

每一个对象都是Object类的实例,所以Array.prototype对象中的_proto_属性指向Object.prototype; 

原型链的查找机制

arr[1]或者 arr.push() 再或者 arr.hasOwnProperty()…

1)首先查找当前实例对象的私有属性,私有中有,获取就是私有的;

2)如果私有中没有,则浏览器默认基于_proto_找其所属类原型(prototype)上的公共属性和方法;

3)如果还找不到,则基于原型对象上的_proto_继续向上查找 … 直到找到Object.prototype为止。最终到null。

例如:arr.push <=> arr._proto_.push <=> Array.prototype.push

1)找到的方法都是相同的;

2)区别是方法执行时候,里面的this 不同;

(1)arr.push() arr首先基于原型链查找机制,找到Array.prototype上的push 方法,并且把方法执行,方法中的this -> arr;

(2)arr._proto_.push() 直接跳过私有属性的查找,找公共的,方法执行的时候,方法中的this -> arr._proto_;

Array.prototype.push() this -> Array.prototype

3)_proto_ 在IE浏览器中进行访问。(那如果代码需要用到_proto_ ,怎么在IE中用呢? 结果:不能用。) 

arr.hasOwnProperty('push') -> false

Array.prototype.hasOwnProperty('push') -> true

公有还是私有属性,它是有参照物的

1)存储在自己的堆内存中的属性是 “私有的”;

2)基于_proto_查找到的属性和方法有“公有的”;

每一个数组“即是数组也是对象”,因为它们可以调用Array.prototype和Object.prototype的属性和方法。
* 构造函数、原型与实例之间的关系

关系解析说明:

每个构造函数都有一个prototype属性指向它的原型对象,原型对象的constructor 指向构造函数,通过new 构造函数 生成实例,实例的__proto__属性指向原型对象。

🌰 原型与原型链

  1. function Fn() { 
  2.     this.x = 100; 
  3.     this.y = 200; 
  4.     this.getX = function () { 
  5.         console.log(this.x); 
  6.     } 
  7. Fn.prototype.getX = function () { 
  8.     console.log(this.x); 
  9. }; 
  10. Fn.prototype.getY = function () { 
  11.     console.log(this.y); 
  12. }; 
  13. let f1 = new Fn; 
  14. let f2 = new Fn; 
  15. console.log(f1.getX === f2.getX); // false 「都是私有的方法」 
  16. console.log(f1.getY === f2.getY); // true 「都是公共的方法」 
  17. console.log(f1.__proto__.getY === Fn.prototype.getY); // true 
  18. console.log(f1.__proto__.getX === f2.getX); // false 
  19. console.log(f1.getX === Fn.prototype.getX); // false 
  20. console.log(f1.constructor);  // fn 
  21. console.log(Fn.prototype.__proto__.constructor); // Object 
  22. f1.getX(); 
  23. f1.__proto__.getX(); 
  24. f2.getY(); 
  25. Fn.prototype.getY(); 

画图分析:( 有图有真相 )

解析说明:

1)先确定执行哪个方法「私有|公有」;

2)再确定执行方法中的this;

3)最后方法执行,计算机需要的结果即可; 

f1.getX()

执行的私有方法,this -> f1

console.log(f1.x) => 100

f1._proto_.getX()

执行的公有方法,this -> f1._proto_

console.log(f1._proto_.x) => undefined

f2.getY()

执行的公有方法,this -> f2

console.log(f2.y) => 200

Fn.prototype.getY()

执行的公有方法,this -> Fn.prototype

console.log(Fn.prototype.y) => undefined

重写内置new以及基于内置类原型扩展方法 

* 核心答案 | 基础知识要夯实

🌰 * 1、new执行的原理 - 面试题( 面试常问 )

  1. function Dog(name) { 
  2.     this.name = name
  3. Dog.prototype.bark = function () { 
  4.     console.log('wangwang'); 
  5. Dog.prototype.sayName = function () { 
  6.     console.log('my name is ' + this.name); 
  7. /* 
  8. let sanmao = new Dog('三毛'); 
  9. sanmao.sayName(); 
  10. sanmao.bark(); 
  11. */ 
  12. function _new() { 
  13.     //=>完成你的代码    
  14. let sanmao = _new(Dog, '三毛'); 
  15. sanmao.bark(); //=>"wangwang" 
  16. sanmao.sayName(); //=>"my name is 三毛" 
  17. console.log(sanmao instanceof Dog); //=>true 

解决方法一( __proto__ 在IE浏览器兼容很差,不建议使用 )

  1. function _new(Ctor, ...params) { 
  2.     // Ctor->Dog params->['三毛'
  3.     let obj = {}; 
  4.     obj.__proto__ = Ctor.prototype; 
  5.     // this->指向创建的实例对象  基于call方法改变即可 
  6.     let result = Ctor.call(obj, ...params); 
  7.     if (/^(object|function)$/.test(typeof result)) return result; 
  8.     return obj; 
  9. }  

解析说明:

1、创建一个实例对象 实例对象.__proto__===所属类.prototype;

2、会把构造函数当做普通函数执行「私有上下文、作用域链、初始THIS、形参赋值...」;

3、观察函数执行的返回值,如果没有返回值或者返回的是基本数据类型值,默认返回的都是实例对象,否则以自己返回的值为主。

Object.create([pro]):创建一个空对象,把[pro]作为当前创建空对象的__proto__的指向(把[pro]作为当前创建空对象的原型)。

1、[pro]可以传递null或者一个对象;

2、如果传递的是null,则当前空对象不具备__proto__的属性,也就是不属于任何类的实例。

🌰 例如:

  1. let pro = { 
  2.     A: 10, 
  3.     B: 20 
  4. }; 
  5. //Uncaught TypeError: Object prototype may only be an Object or null: undefined 
  6. console.log(Object.create());  
  7. console.log(Object.create(null)); 

解决方法二( 在IE6,7,8浏览器不兼容 )

  1. function _new(Ctor, ...params) { 
  2.     let obj = Object.create(Ctor.prototype); 
  3.     let result = Ctor.call(obj, ...params); 
  4.     if (/^(object|function)$/.test(typeof result)) return result; 
  5.     return obj; 
  6. }  

解决方法三 ( 兼容性比较好 )

  1. // 重写的方法只考虑pro传递的是一个对象 
  2. Object.create = function (pro) { 
  3.     function Proxy() {} 
  4.     Proxy.prototype = pro; 
  5.     return new Proxy; 
  6. }; 
  7.  
  8. function _new(Ctor) { 
  9.     // 获取除第一个实参以外,剩余传递的参数信息,以数组的形式保存到params中 
  10.     var params = [].slice.call(arguments, 1); 
  11.     // Object.create兼容IE低版本浏览器,需要改写 
  12.     var obj = Object.create(Ctor.prototype); 
  13.     // 基于apply既可以改变this,也可以把数组中的每一项传递给函数 
  14.     var result = Ctor.apply(obj, params); 
  15.     if (/^(object|function)$/.test(typeof result)) return result; 
  16.     return obj; 

* 2、扩展内置类原型上的方法

1、调用的时候更加方便;

2、也更好的实现链式调用;

* 注意:自己编写的方法会覆盖内置的方法,所以自己命名的时候需要注意,一般都是设置前缀,例如:myUnique。

🌰 常用数组去重方法

  1. function unique(arr) { 
  2.     // 首先基于Set结构去重,最后转换为数组 
  3.     let result = new Set(arr); 
  4.     result = Array.from(result); 
  5.     return result; 
  6. let arr = [1, 2, 3, 2, 3, 4, 2, 3, 4, 2, 1, 2, 3, 4, 5, 3, 4]; 
  7. let result = unique(arr); 
  8. console.log(result);  

🌰 先去重,再排序 - 面试题( 面试常问 )

  1. Array.prototype.unique = function unique() { 
  2.     // this->arr 一般是当前操作类的实例 
  3.     let result = new Set(this); 
  4.     result = Array.from(result); 
  5.     return result; //返回的结果还是一个数组,则可以继续调用数组的其它方法 ->“链式调用” 
  6. }; 
  1. // 先去重,再排序 
  2. //  + sort是Array.prototype上的方法,所以数组可以直接调用 
  3. let arr = [1, 2, 3, 2, 3, 4, 2, 3, 4, 2, 1, 2, 3, 4, 5, 3, 4]; 
  4. let result = arr.unique().sort((a, b) => a - b); 
  5. console.log(arr, result); 

THIS情况汇总及CALL、APPLY、BIND的应用 

* 核心答案 | 基础知识要夯实

1、THIS的几种情况

1)事件绑定;

2)函数执行:1)自执行函数 2)回调函数;

3)构造函数执行;

4)基于call /apply /bind 改变函数中的this;

5)箭头函数中没有自己的this,所用到的this是使用其上下文中的;

说明:Function.prototype -> call/apply/bind 所有的函数都可以调取这三个办法。

  1. Function.prototype.call = function call(context) { 
  2.     // this->fn 
  3.     // context->obj 
  4.     // ... 
  5. };  

🌰 基于call /apply /bind 改变函数中的this

  1. window.name = 'WINDOW'
  2. let obj = { 
  3.     name'前端学苑'
  4.     age: 2 
  5. }; 
  6.  
  7. function fn(x, y) { 
  8.     console.log(this, x + y); 
  9. }  
  10.  
  11. fn(); //this->window 
  12. obj.fn(); //Uncaught TypeError: obj.fn is not a function 
  1. fn.call(obj); //this->obj   
  2. fn.call(obj, 10, 20); //this->obj x->10 y->20 
  3. fn.call(); //this->window 严格模式下undefined 
  4. fn.call(null); //this->window 严格模式下null 「传递的是undefiend也是如此」 
  5. fn.call(10, 20); //this->10「对象」 x->20  y->undefined 

解析说明:

fn.call(obj);

底层处理方式:fn先基于__proto__找到Function.prototype.call,把call方法执行的时候,call方法内部实现了一些功能:会把fn执行,并且让fn中的this变为第一个实参值。

* apply的作用和细节上和call一样,只有一个区别:传递给函数实参的方式不一样。

  1. fn.call(obj, 10, 20); 
  2. fn.apply(obj, [10, 20]);  

最后结果和call是一样的,只不过apply方法执行的时候要求:传递给函数的实参信息都要放置在一个数组中,但是apply内部也会向call方法一样,把这些实参信息一项项的传递给函数。

🌰 需求:获取数组中的最大值

  1. let arr = [10, 30, 15, 36, 23]; 

* 方法一:先排序

  1. arr.sort(function (a, b) { 
  2.     return b - a; 
  3. }); 
  4. let max = arr[0]; 
  5. console.log('数组中的最大值是:' + max);  

* 方法二:假设法

第一种方法:

  1. let max = arr[0]; 
  2. for (let i = 1; i < arr.length; i++) { 
  3.     let item = arr[i]; 
  4.     if (item > max) { 
  5.         max = item; 
  6.     } 
  7. console.log('数组中的最大值是:' + max);  

第二种方法:

  1. let max = arr.reduce((result, item) => { 
  2.     return item > result ? item : result; 
  3. }); 
  4. console.log('数组中的最大值是:' + max); 

* 方法三:借用Math.max

第一种方法:

  1. Math.max(10, 30, 15, 36, 23) ->36 获取一堆数中的最大值 
  2. Math.max([10, 30, 15, 36, 23]) ->NaN 传递一个数组是不行的 

第二种方法:ES6展开运算符

  1. let max = Math.max(...arr); 
  2. console.log('数组中的最大值是:' + max); 

第三种方法:基于apply的特点

  1. let max = Math.max.apply(null, arr); 
  2. console.log('数组中的最大值是:' + max); 

第四种方法:字符串拼接成为最终想要的表达式

  1. let str = `Math.max(${arr})`; 
  2. let max = eval(str); 
  3. console.log('数组中的最大值是:' + max); 

🌰 需求:把类数组集合转换为数组集合

重写内置的slice,实现浅克隆;

  1. Array.prototype.slice = function slice() { 
  2.     // 重写内置的slice,实现浅克隆 
  3.     // this->ary 
  4.     let arr = []; 
  5.     for (let i = 0; i < this.length; i++) { 
  6.         let item = this[i]; 
  7.         arr.push(item); 
  8.     } 
  9.     return arr; 
  10. }; 
  11. let ary = [10, 20, 30]; 
  12. let newAry = ary.slice(); //不传递或者传递0 -> 数组的浅克隆 
  13. console.log(newAry, newAry === ary);  

画图分析:( 有图有真相 )

区别在于:

1、内置代码中用的是this,自己写的代码中用的是argument。

2、如果我们可以 让内置的slice执行,并且把方法中的 this改变为arguments -> 这样其实就是把类数组集合克隆(转换)为数组。

3、让内置slice执行:找到slice,加小括号一执行即可。

1)Array.prototype.slice() 2)[].slice()

改变方法中的this

1)call 2)apply 

4、[].slice.call(arguments) 把类数组转换为数组

重要:数组中大部分方法,都可以基于这样的原理(改变this),实现类数组的借用。原因:类数组除了不是Array的实例,和数组的结果是一致的,所以操作数组的一些代码(类似于循环等操作)也一定适用于类数组,所以可以实现方法的借用。

求和第一种方法:

  1. function sum() { 
  2.     // arguments:实参集合,它是一个类数组,不是Array的实例,所以不能直接调用Array.prototype上的方法,但是结构和数组非常的相似,都是索引+length 
  3.     // 第一种方法 
  4.     let arr = []; 
  5.     for (let i = 0; i < arguments.length; i++) { 
  6.         let item = arguments[i]; 
  7.         arr.push(item); 
  8.     } 
  9.     // 第二种方法 
  10.     // let arr = [].slice.call(arguments); 
  11.     return arr.reduce((result, item) => item + result); 
  12. }  
  13. let total = sum(10, 20, 30, 40); 

求和第二种方法,ES6实现:

  1. function sum(...arr) { 
  2.     // 第一种方法:... arr 基于剩余运算符获取的实参集合本身就是一个数组 
  3.      
  4.     // 第二种方法:Array.from:可以把一个类数组(或者Set)转换为数组 
  5.     // let arr = Array.from(arguments); 
  6.  
  7.     // 第三种方法:基于展开运算符把类数组中的每一项拿出来,分别赋值给数组 
  8.     // let arr = [...arguments]; 
  9.     return arr.reduce((result, item) => item + result); 
  10.  
  11. let total = sum(10, 20, 30, 40); 
  12. console.log(total);   // 结果:100 

🌰 this指向的例子

  1. let obj = { 
  2.     name'前端学苑'
  3.     age: 11 
  4. }; 
  5.  
  6. function fn(x, y) { 
  7.     console.log(this, x, y); 
  8. }  

1、操作一:

  1. document.body.onclick = fn; 

分析:

1)事件绑定的时候方法是没有执行的,只有事件触发,浏览器会帮助我们把方法执行;

2)this->body;

3)x->MouseEvent 事件对象「浏览器不仅帮助我们把方法执行,而且还把存储当前操作的信息的事件对象传递给函数」;

4)y->undefined;

2、操作二:

  1. setTimeout(fn, 1000); 

分析: 

1)设置一个定时器(此时绑定的函数没有执行,此时只是绑定一个方法),1000MS后,浏览器会帮助我们把fn执行;

2)this->window;

3)x->undefined;

4)y->undefined;

我们期望:不论是事件触发,还是定时器到时间,执行对应的方法时,可以改变方法中的this,以及给方法传递实参信息。

1、“立即处理的思想”

直接下属这种操作办法是不可以的:call/apply在处理的时候,会把函数立即执行,也就是在事件绑定或者设置定时器的时候,fn就执行了,而不是等待事件触发或者定时器到时间后再执行 “立即处理的思想”。

代码如下:

  1. document.body.onclick = fn.call(obj, 10, 20); 
  2. setTimeout(fn.call(obj, 10, 20), 1000); 

2、“预先处理思想「柯理化函数」”

我们绑定方法的时候(不论是事件绑定还是设置定时器),先绑定一个匿名函数,事件触发或者达到时间,先把匿名函数执行,在执行匿名函数的时候,再把我们需要执行的fn执行,此时就可以基于call/apply改变this和参数信息了。

代码如下:

  1. document.body.onclick = function (ev) { 
  2.     //this->body 
  3.     fn.call(obj, 10, 20, ev); 
  4. }; 
  5. setTimeout(function () { 
  6.     //this->window 
  7.     fn.call(obj, 10, 20); 
  8. }, 1000);  

bind相当于call/apply来讲,并不会把函数立即执行,只是实现处理了要改变的this和参数,一切的执行还是按照原有的时间或者触发节点进行。

代码如下:

  1. document.body.onclick = fn.bind(obj, 10, 20); 
  2. setTimeout(fn.bind(obj, 10, 20), 1000); 

* 箭头函数没有自己的this

🌰 实例一:

  1. let obj = { 
  2.     name'前端学苑'
  3.     age: 11, 
  4.     fn: function () { 
  5.         // this->obj 
  6.         let that = this; 
  7.         return function () { 
  8.             // this->window 
  9.             // 如果需要改变obj.name,可以用that替换this 
  10.             that.name = 'FE2020'
  11.             console.log(this); 
  12.         }; 
  13.     } 
  14. }; 
  15. let f = obj.fn(); 
  16. f(); 

🌰 实例二:

  1. let obj = { 
  2.     name'前端学苑'
  3.     age: 11, 
  4.     fn: function () { 
  5.         // this->obj 
  6.         return () => { 
  7.             this.name = 'FE2020'
  8.             console.log(this); // {name:'FE2020', age: 11, fn:f} 
  9.         }; 
  10.     } 
  11. }; 
  12. let f = obj.fn(); 
  13. f.call(100);  

说明:

箭头函数没有this(方法执行的时候不存在初始this这一项操作),所以基于call/apply操作它都是无用的,没有this。

责任编辑:姜华 来源: 前端学苑
相关推荐

2019-02-27 16:00:48

JS原型原型链对象

2020-02-20 14:00:15

JavaScript原型原型链

2010-10-08 09:13:15

oop模式JavaScript

2012-11-08 10:40:47

JavaScript原型链

2023-08-28 07:12:54

2017-05-05 10:31:35

JavaScriptprototype__proto__

2017-04-07 11:15:49

原型链原型Javascript

2012-01-05 15:07:11

JavaScript

2012-12-13 11:01:42

IBMdW

2016-06-07 14:28:39

Javascript原型

2022-05-26 09:20:01

JavaScript原型原型链

2020-09-10 07:04:30

JSJavaScript 原型链

2022-03-29 09:15:55

Javascript函数属性

2022-05-26 23:14:26

原型原型链JS继承

2016-05-06 14:02:18

JavaScript原型链

2011-08-24 13:51:56

JavaScript

2016-12-27 09:10:29

JavaScript原型链继承

2011-08-31 14:48:33

JavaScript

2015-06-09 10:55:58

JavaScriptinstanceof运

2013-09-18 14:01:46

JavaScript
点赞
收藏

51CTO技术栈公众号