通过调用标识符确定this
一. 纲
this的性质
作用:表示函数执行时的环境
值:一个对象
特点:动态性
确定this的难度
- JS语言的动态性:
函数的this在执行时才能确定
- 函数为一级公民
可作实参、返回值、数据赋值进行传递
解决方法
- 书写函数时,要预想该函数会被如何调用
- 以调用标识符确定this值
- 以变量代替this、使用绑定
函数的调用标识符
1. 函数是复杂数据类型
2. 变量可获取函数值--引用地址
3. 函数调用时,该调用名字是该函数此次调用时的标识符
以标识符确定this值
唯一能确定this的途径:函数的调用标识符
- 未使用绑定字: 函数的调用标识符是否属于一个对象的成员
是:this就指向该对象
否:宽松模式是window;严格模式是未定义
- 使用绑定字 (执行时) : 把this绑定到指定对象上
开启严格模式
在函数体内使用严格关键词
class方法与模块方法自动运行在严格模式中
不包括方法简写
例
// 伪代码
//以下fn均表示一個宽松环境的函数, obj表示一个对象
fn(); // window
obj1.fn(); // obj1
obj2.obj3.fn() // obj3
fn.call(obj4) // obj4
new fn() // 实例
函数
fn
执行时,其调用标识符均是fn
每个标识符
fn
所属的对象都不同,其this
就不同
fn()
: 调用标识符fn不属于任何对象:this
是window
obj1.fn()
: 调用标识符fn属於对象obj1
的成员:
this
是obj1
obj2.obj3.fn()
: 调用标识符fn属於对象obj2
的成员:
this
是obj3
未使用绑定时,函数的调用标识符属于对象的一个成员时,其
this
就是该对象
二. 未使用绑定字
确定 this 值 :
只要查看该函数调用时的标识符
该标识符决定当前函数执行时的this值
1 函数自我调用
调用标识符是该函数本身的标识符
该标识符不属于任何对象的属性
此时this指向
宽松模式:全局对象
window、global(node环境)·
严格模式: this为未定义
立即执行函数也是函数自我调用,
其调用标识符是
anonymou
其this,永远是window/未定义
2 一个函数的多种调用形式
函数的引用地址被赋值给一个对象的属性
函数调用时: 若调用标识符属于对象的一个成员
this就指向该对象
例:
1.函数作为数据进行赋值
// 伪代码: fn表示宽松模式的下的函数
const A = {
fn: fn
};
const B = {
b1: A.fn,
b2: A
};
var c = objA.fn;
fn(); // window
A.fn(); // A
B.b1() // B
B.b2.fn(); // A
c(); // window
函数的调用属于地址引用,可赋值给不同变量
调用函数时的标识符 ,决定了此函数执行时的其内部的this值
分析表
调用形式 调用标识符 标识符所属对象 this fn0()
fn0
无 window
A.fn()
fn()
A
A
B.b1()
b1()
B
B
B.b2.fn()
fn()
b2
,b2
的值是A
A
c()
c()
无 window
2. 函数作为数据进行赋值
var obj = {
y: function fn1(x) {
console.log(this);
while (x) {
fn1(x - 1);
break;
}
},
z: function fn2(x) {
console.log(this);
while (x) {
obj.z(x - 1);
break;
}
},
};
obj.y(1); //obj 、 window"
obj.z(1); //obj 、window"
调用形式 | 调用标识符 | 标识符所属对象 | this |
---|---|---|---|
obj.y(1) |
y() |
obj |
obj |
fn1(x - 1) |
fn1() |
无 | window |
obj.z(1) |
z() |
obj |
obj |
obj.z(x-1) |
z() |
obj |
obj |
3.函数作为参数传入
1.参数传递是一种隐式赋值
2.传参:类似于:
argument = A.fn ;
参数cb获取了函数fn的引用
// 伪代码: fn表示宽松模式的下的函数
let A = {
fn,
fn2: (function () {
console.log(this);
})()
},
B = {};
function b(cb) {
console.log(this); // window
B.fn = cb;
cb(); // window
B.fn(); // B
}
A.fn(); // A
b(fn);
调用形式 | 调用标识符 | 标识符所属对象 | this |
---|---|---|---|
A.fn() |
fn() |
A |
A |
b(fn) |
b() |
无 | window |
cb() |
cb() |
无 | window |
B.fn() |
fn() |
B |
B |
3.参数所属对象要看情况
1.如在浏览器方法中的定时器
其回调函数参数是属于window
对象的成员
回调函数this
是window
对象
var obj = {
fo: function fo() {
'use strict'
console.log(this);
}
};
obj.fo(); // obj
setTimeout(obj.fo, 100); // window
2.如事件监听
其回调参数所属对象是元素本身
其他
getter 与 setter函数同理
实例中继承的方法同理
class语法的super调用的方法中的this
1.super是静态的,指向当前类的基类
2.但是super调用的方法时,进行了绑定
把this绑定到使用
super·
的类例
function fn() { };
let obj = {};
Function.prototype.f1 = function () {
console.log(this);
return function f2() {
console.log(this);
}
}
Function.prototype.x(); // Function.prototype
Function.x() // Function
obj.f = fn.f1(); //fn
obj.f(); // obj
fn.f1()(); // window
let f2 = fn.f1();
f2(); // window
调用形式 | 调用标识符 | 标识符所属对象 | this |
---|---|---|---|
Fn.proto.x() |
x() |
Fn.proto |
Fn.proto |
fn.f1() |
f1() |
fn |
fn |
f2的this,同样只能在其被调用时决定
不在于它被赋值给谁
调用形式 | 调用标识符 | 标识符所属对象 | this |
---|---|---|---|
obj.f() |
f() |
obj |
obj |
fn.f1()() |
匿名函数() |
无 | window |
f2() |
f2() |
无 | window |
3.事件处理
内联事件:
事件触发函数执行,该函数的调用标识符决定
this
传入this实参
内联事件中的this实参是该事件的元素本身
<button onclick="fn(this)">AMTF</button>
<button onclick="obj.fn(this)">JLSJ</button>
function fn(v) {
console.log(this, v); // window、 button-AMTF
}
let obj = { fn }; // obj、 button-JLSJ
document.getElementsByTagName("button")[0]
.onclick = function () {
console.log(this); // button-AMTF
}
外联事件与事件监听
相当于把函数赋值给该元素的某个属性
触发该函数执行时,调用者是这个元素
this指向调用该函数的元素--
e.currentTarget
btn.addEventListener('click', fn);
function fn(e) {
if (e.target == this) {
// 只在元素本身被觸發時執行
// doSomething...
console.log(e.target, this);
}
}
IE的
attachEvent
的回调函数的this是window
修改this:把函数赋值给对象,再执行这个对象的方法
function attachEvent (elm, event, cb) {
elm.attachEvent(("on" + event), function () {
elm.cb = cb;
elm.cb(window.event);
delete elm.cb;
});
}
// 或者使用绑定
function attachEvent(elm, event, cb) {
elm.attachEvent(("on" + event), cb.bing(elm, window.event));
}
三.使用绑定
确定this值: 绑定关键字指定对象即为this值
同样只能在函数执行时确定
四个绑定关键词: new、call、apply、bing
1.call
与apply
直接指定一个对象作为 this 的值
可使用
apply
来展开一个数组ES5:可传入类数组对象
2. 硬绑定bing
- 原理
function fakeBind(fn, obj) {
return function () {
return fn.apply(obj, arguments);
};
}
- 返回一个新函数
- 新函数与原函数的函数体相同
- 但其this被绑定到指定的对象
在使用了
bing
的函数是尚未执行的,其
this
是不确定的: 只能在执行时确定使用new调用:为
new
创建的对象没使用new调用:为
bing
绑定的对象只会被
new
改变
3. 构造器的new
绑定
一定會用新创建的实例对象替换原來的this
无论该函数是否已被其他绑定字绑定过
4. 自带绑定
JS、库、宿主环境的内置函数,提供一个参数
用于绑定 this值
例:数组的forEach
的参数2
5. 绑定到空/原始值
- 不错的空对象:
Object.create(null);
- 绑定到
null / undefined
- 宽松模式:绑定到全局对象
- 严格模式:
null / undefined
- 绑定到原始值(字符串类型、布尔类型或者数字类型)会封装成对应的对象
new String、new Boolean、new Number
let o = {
a: function () {
console.log(this);
}
};
o.a(); // o
o.a.call(null); // window
o.a.call(1); // Number(1)
四. 箭头函数
确定this值 :
箭头函数没有this值
通过引用其上属函数的this值
若该箭头函数没有上属函数,则其this值为未定义
无法使用绑定关键词修改箭头函数的this值
因为他没有this值
唯一修改this值:
给他套上普通函数,再修改该普通函数的this
function x() {
return (a) => {
//this 取自 x()
console.log(this.a);
};
}
var oA = { a: 2 };
var oB = { a: 3 };
var y = x.call(oA);
y.call(oB); // 2
由于x
的 this
绑定到 oA
箭头函数的this
指向 oA
: y的this
也指向 oA
- 确定箭头函数的
this
:- 没有所属函数时,只能是未定义
- 有所属函数,取决于所属函数执行时的
this
五.例
1.闭包函数
在一个函数中返回另一个函数
function f1() {
console.log(this);
return function f2() {
console.log(this);
}
}
f1()();
函数f1返回的函数的this值,由调用时决定
f1( )( )·
:两次this
都是指向了window
f2执行时,如立即执行函数
const obj = {};
obj.y = f1();
obj.y();
let fn = fn1();
fn();
把f1的返回值,赋值给一个变量
f2的this:调用时决定--fn()、obj.y()
调用标识符所属的对象不相同
其 f2的this也不相同
2. 反柯里化:
反柯里化:让对象去借用一个原本不属于它的方法
普通函數寫法
Function.prototype.uncurrying = function () {
var self = this;
return function () {
let thisArg = [].shift.call(arguments);
return self.apply(thisArg, arguments);
}
}
箭頭函數寫法
Function.prototype.uncurrying = function () {
return (...args) => this.call( ...args)
}
箭头函数的this是取值其外层函数的this,
外层函数
uncurrying
的this值,决定于调用时的
let push = Array.prototype.push.uncurrying();
- 此时
uncurrying
的调用者是push
函数
则此时的this
是指向push
函数
3. bing的实现
- 工具函数
objectCreate = Object.create || function (proto) {
var F = function () { };
F.prototype = proto;
return new F();
}
let slice = Array.prototype.slice.uncurrying();
let concat = Array.prototype.concat.uncurrying();
- 实现逻辑
Function.prototype.bind = function (thisArg) {
if (typeof this !== "function") {
throw new TypeError("被绑定的不是函數");
}
var self = this;
var baseArgs = slice(arguments, 1);
var bind = function () {
return self.apply(
this instanceof self ? this : thisArg,
concat(baseArgs, slice(arguments))
);
}
bind.prototype = objectCreate(self.prototype);
return bing;
};
4. 自定义默认绑定对象
实现:修改例3的bind
函數的返回值的apply
的参数1
默认绑定--只当this
指向全局对象或者undefined
时
把this绑定到默认的对象
return self.apply(
((!this || this === (window || global)) ?
thisArg : this
),
concat(baseArg, slice(arguments))
);
对apply
参数1的定制,可实现多种绑定
5. 多重綁定
const obj1 = {
name: "obj1"
};
const obj2 = {
name: "obj2"
};
function f1() {
console.log(this);
}
function f2() {
f1.call(this);
}
const f3 = () => f2.call(this);
f2.call(obj1); // obj1
f3.call(obj2); // window
f2 .call(obj1 )
:
f2
的this被綁到了obj1
,所以f1.call(this)
的this是obj1
;使得f1
的this為obj1
f3.call(obj2)
:
f3
是箭頭函數--this綁定無效,但其沒有所屬的函數,所以f2.call(this)
的this值為未定義
而在寬鬆模式下的綁定到未定義會改爲绑定到全局对象
6. 立即执行函数
let o = {
a: (function () {
console.log(this); // window
return function () {
console.log(this); // 看情况:调用标识符决定
}
})()
};
(function (fn) {
console.log(this); // o
fn.call(this); // window
(() => console.log(this))(); // o
}).call(o, () => console.log(this));
立即执行函数的this
- 未使用绑定时,永远是window/未定义
- 执行时使用绑定,是被绑定的对象
- 箭头函数,其this如同继承的属性,取自其所属的函数
7.函数式编程相关
在函数中返回一个对象,进行链式调用
let x = function () {
let obj = { y, z }
return obj; // 等效于 return { y, z };
function y() { console.log(this); return this }
function z() { console.log(this); return this }
};
x().y().z();
因为返回的是对象
x() === obj
,而x().y() === obj.y()
x().y() === obj
,而x().y().z()=== obj.z()
通过调用标识符确定this的更多相关文章
- JSON-RPC轻量级远程调用协议介绍及使用
这个项目能够帮助开发人员利用Java编程语言轻松实现JSON-RPC远程调用.jsonrpc4j使用Jackson类库实现Java对象与JSON对象之间的相互转换.jsonrpc4j包含一个JSON- ...
- JSON-RPC远程调用协议
1. JSON-RPC简介 2. 请求 3. 响应 4. 错误 4.1. 错误对象 4.2. 错误码 5. 批量调用 6. 示例 6.1. 列表形式参数 6.2. key-value形式参数 6.3. ...
- js 闭包 理解
1.什么是闭包 定义:是指有权访问另一个函数作用域中的变量的函数 创建闭包:在一个函数内部创建另一个函数 基本特点 在返回的匿名函数中 可以调用外部函数的变量 如下例中所示 内部函数(匿名函数) 可以 ...
- 关于C语言的一些trick
很多东西已经记不起来了,想到一点写一点,碰到一点写一点,慢慢累积. 关于# #在宏定义中用于替换传入变量的字符,例如: #define whole_operation(n) do { printf( ...
- Atitit.js javascript的rpc框架选型
Atitit.js javascript的rpc框架选型 1. Dwr1 2. 使用AJAXRPC1 2.2. 数据类型映射表1 3. json-rpc轻量级远程调用协议介绍及使用2 3.1. 2.3 ...
- php static延迟静态绑定
如果你是一个懒惰的程序员,你看到以下代码可能会恼火 abstract class U{ } class u1 extends U{ public static function create(){ r ...
- angularjs入门学习【指令篇】
一.首先我们来了解下指令API 属性 含义 restrict 申明标识符在模版中作为元素,属性,类,凝视或组合,怎样使用 priority 设置模版中相对于其它标识符的运行顺序 Template 指定 ...
- [机翻] WIRER ON THE WIRE - SIGNALR协议的非正式描述
原文 原文很简单,以下为机翻 WIRER ON THE WIRE - SIGNALR协议的非正式描述 我已经看到询问有关SignalR协议的描述的问题出现了很多.哎呀,当我开始关注SignalR时,我 ...
- php分享二十二:php面向对象
1:static访问符 在类中使用static有两种主要用途.定义静态成员和定义静态方法.静态成员只保留一个变量的值,这个值对所有实例都是有效的 类的方法是static的,他所访问的属性也必须是sta ...
随机推荐
- 怎么写简历,简历才不会被丢到非洲🌍
前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 最近的三歪朋友圈可以看到很多的字节.腾讯的同学都 ...
- Mybatis 和 Solon 勾搭在一起,也是个漂亮组合
故事相关的源码 https://gitee.com/noear/solon_demo/tree/master/demo08.solon_mybatis 故事开讲 Solon 是Java世界里一个新的极 ...
- Caused by: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value f ...
- 【特别篇】不为人知的U盘秘密
U盘是我们代码爱好者的必要东西,方便于我们更好的拷文件,使用一些已经配置好的东西,比如说:小编经常会将linux系统放进去,平时就可以随时用了. But 你的U盘真的正常吗?你了解多少? 关于U盘 ...
- 偏序 分块+bitset
题目描述 给定一个有\(n\)个元素的序列,元素编号为\([1,n]\),每个元素有\(k\)个属性\(p_1,p_2,p_3,...,p_k\) ,求序列中满足 \(i<j\)且 \(1 \l ...
- 教育行业CRM项目开发
项目开发流程 需求分析 存储所有的客户咨询信息 避免重复数据 客户多次跟踪记录 客户来源分析.成单率分析 每个销售只能修改自己的客户信息 报名流程开发 班级 ...
- Mybatis Log plugin 破解!!!
前言 今天重新装了IDEA2020,顺带重装了一些插件,毕竟这些插件都是习惯一直在用,其中一款就是Mybatis Log plugin,按照往常的思路,在IDEA插件市场搜索安装,艹,眼睛一瞟,竟然收 ...
- 报错:ER_NO_DEFAULT_FOR_FIELD: Field 'status' doesn't have a default value
小白入门级错误,数据库插入数据时报错;ER_NO_DEFAULT_FOR_FIELD: Field 'status' doesn't have a default value 百度说是my.ini文 ...
- 符合SEO的网站标题应该怎么写
http://www.wocaoseo.com/thread-96-1-1.html 的seo网站标题既能提起读者的点击欲望,又能搜索引擎中获得好的排名,这两着之间有着有有一些联系,网站的标题若要从s ...
- Java中解析wav音频文件信息:音频声道数,采样频率,采样位数、声音尺寸
前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 音频解析方法: public static int toInt(byte[] b) { return ((b[3] << 2 ...