学习JavaScript第十一天(函数的三种角色,this的五种情况)
程序员文章站
2022-04-09 17:16:22
学习JavaScript第十一天(函数的三种角色,this的五种情况)函数的三种角色/* * 函数的三种角色 + 普通函数:闭包作用域 + 构造函数:可以通过 new 执行创建其实例 + 普通对象:有键值对(也有原型链__proto__,指向Function.prototype这个匿名空函数) 这三种角色之间没有必然联系 所有的函数都是内置类Function的实例,所以一般函数的原型的constructor都是Function,即 fun.prototype...
学习JavaScript第十一天(函数的三种角色,this的五种情况)
函数的三种角色
/*
* 函数的三种角色
+ 普通函数:闭包作用域
+ 构造函数:可以通过 new 执行创建其实例
+ 普通对象:有键值对(也有原型链__proto__,指向Function.prototype这个匿名空函数)
这三种角色之间没有必然联系
所有的函数都是内置类Function的实例,所以一般函数的原型的constructor都是Function,即 fun.prototype.constructor: Function
由于函数的原型是一个对象,所以 fun.prototype.__proto__: Object.prototype
*/
/*
* Object 与 Function 的关系
+ 内置类Object是Function的一个实例;
+ Function也是Object的一个实例
+ Object还是Object的一个实例
+ Function也是Function的一个实例
即:
Object.__proto__.__proto__ === Object.prototype
Function.prototype === Function.__proto__
* 在 Function.prototype 上有call/apply/bind三个改变this指向的方法,所有函数都可以调用这三个方法
*/
/* function Foo(){
getName = function(){
console.log(1);
};
return this;
}
Foo.getName = function(){
console.log(2);
}
Foo.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5)
}
Foo.getName();
// 把Foo看做对象,执行其私有方法
// => 2
getName();
// 执行全局的方法,在代码执行到 var getName = ...时,其创建的新值覆盖变量提升时的全局getName
// => 4
Foo().getName();
// 执行Foo改变全局的getName的值后返回一个对象(winfow),再执行返回这个对象的getName
// window.getName()
// => 1
getName();
// 全局getName的值在Foo()的时候被重新赋值了
// => 1
new Foo.getName();
// 成员访问的优先级高于不带参数的 new
// 等价于 new (Foo.getName),把Foo.getName 执行,并返回一个实例对象
// => 2
new Foo().getName();
// 带参数列表的new(new xx()),跟成员访问的优先级一样,则从左往右进行计算
// 等价于 (new Foo).getName new Foo执行返回Foo的一个实例,执行 实例.getName
// => 3
new new Foo().getName();
// 无法参数列表的new的优先级低于带参数列表的new和成员访问的优先级
// 等价于 new [(new Foo).getName]
// 即 new (Foo实例.getName) 把Foo实例.gerName 执行,并返回一个实例对象
// >= 3 */
this的五种情况汇总
/*
* JS中 this 的五种情况汇总
1、事件绑定:
给当前元素的某个事件绑定方法,当事件行为触发,方法被执行,方法中的this一般都是当前操作的元素
排除:IE6~8中,基于attachEvent进行的DOM2事件绑定,方法中的this是window
2、函数执行(包括自执行函数)
函数执行,看函数前面有没有点“.”,有点,则点前面是谁,函数中的this就是谁;没有点,则this是window
+ 在严格模式下,没有点,this是undefined
+ 匿名函数(自执行函数/回调函数)执行,一般this也是window(严格模式下是undefined),除非有特殊处理
3、new 构造函数
构造函数执行(new xxx),函数体中的this是当前类的实例
4、箭头函数
ES6中的箭头函数(或者基于{}形成的块级上下文),里面没有this,如果代码中遇到this也不是它自己的,而是它所在上下文中的this
5、call/apply/bind
基于Function.prototype上的call/apply/bind方法强制改变函数中的this执行
+ 对箭头函数没用,因为箭头函数中没有自己的this
*/
// 情况一:
/* document.body.onclick = function(){
console.log(this); // body元素
}
document.body.addEventListener('click', function(){
console.log(this);// body元素
}) */
// 情况二:
// "use strict"
/* function fn(){
console.log(this);
}
let obj = {
name: 'obj',
fn: fn
}
//函数前面没有点
fn(); // window/undefined
// 函数前面有点
obj.fn(); // obj
// 自执行函数
(function(){
console.log(this); // window/undefined
})();
// 回调函数
[1,2].forEach(function(a,b){
console.log(this); // window/undefined
}); */
// 情况三:
/* function Fn(){
this.name = '构造函数';
console.log(this);
}
Fn.prototype.sum = function(){}
let f = new Fn; // this -> f
Fn(); // this -> window
f.sum(); // this ->f
f.__proto__.sum(); // this -> f.__proto__ */
// 情况四:
/* let obj = {
name: 'obj',
fn: function(){
// obj.fn()执行: this ->obj
console.log(this);
setTimeout(function(){
console.log(this); // window 因为这是一个回调函数
},500);
setTimeout(() => {
// 这里的this用的是上级上下文中的this
console.log(this); // obj
}, 1000)
}
}
obj.fn(); // fn中的this-> obj */
// 情况五:
let obj = {
name: 'obj',
fn:fn
};
function fn(){
console.log(this);
}
fn(); // window
obj.fn(); // obj
fn.call(obj); // obj
console.log(fn.call(obj)); // undefined
obj.fn.apply(window); // window
/*
* call/apply/bind 的使用
=== call ===
函数.call(context, parm1, parm2, ...)
简单说明:把函数执行,让函数中的this指向context,并且把parm1、parm2...作为实参传递给函数
详细说明:
+ 首先函数基于原型链__proto__找到Function.prototype.call方法,并且把call方法执行
+ call方法中的this就是当前操作的Function的实例---函数,传递给call方法的第一个实参是要函数中this的指向,剩余实参是依次要传递给函数的参数
+ call方法执行的过程中,实现了这样的处理:把 函数[call中的this] 执行,让 函数中的this指向context,并且把剩余实参传递给函数
如果一个参数也不传,或者第一个参数传递的是null/undefined,给严格模式下,最后函数中的this都是window(严格模式下,不传是undefined,传了null/undefined,最后函数中的this也会改为对应的值)
=== apply ===
函数.apply(context, [parm1, parm2,...])
对比call和apply的区别只有一个,执行函数的时候,需要传递给函数的参数信息,在最开始传递给call/apply的时候,形式不一样
+ call 是需要把参数一个一个的传递给call
+ apply 是需要把参数放在一个数组中传递给apply
=== bind ===
call/apply在执行的时候,都会立即把要操作的函数执行,并且改变它的this指向
bind 是预先处理:指向bind只是预先把函数中需要改变的this等信息改变了并存储起来,此时函数并不会被立即执行,执行完bind会返回一个匿名函数,当后期执行匿名函数的时候,再去把之前需要执行的函数执行,并且改变this的指向
*/
let a = fn.bind(obj); // 执行bind的时候,fn是不会执行的
console.log(a); // 执行bind会返回一个函数,后期只有手动调用a()才会把里面的fn执行,并且按照执行bind时传递的this指向改变fn中的this值
// 需求:1s后执行fn,并且让fn中的this变为obj,传递10,20
setTimeout(fn.call(obj, 10, 20), 1000); // 这样写虽然this和参数都是想要的,但是并没有在1s后才执行,而是立即就执行了,这是因为在设置定时器的时候,已经基于call方法把fn给执行了
setTimeout(fn.bind(obj, 10, 20), 1000); // 把bind执行完后的结果(一个匿名函数)绑定给定时器,1s之后执行这个匿名函数
本文地址:https://blog.csdn.net/qq_32021429/article/details/109244346
上一篇: 博客园css定制