javascript 函数的4种调用方式与 this(上下文)的指向
前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!
javascript 中作用域链和 this(上下文)的指向是很容易混淆的,简单的说就是:
- 作用域链取决于函数声明的位置,函数声明之后,从函数内部往外,一直到window,这就是它的作用域链,与函数调用位置无关;
- this 指向函数调用时的对象,如果是独立调用,那就是指向 window,与函数声明的位置无关;
函数调用的方式有4种,this 也就有4种指向:
- 独立调用:func(),函数独立调用,this指向window,;
- 方法调用:obj.func(),函数作为obj的一个方法(属性)调用,this指向obj;
- 构造函数调用:new Func(),如果在一个函数前面带上 new 关键字来调用, 那么背地里将会创建一个连接到该函数的 prototype 的新对象,this指向这个新的对象;
- call、apply、bind调用:func.call(obj,value1,value2); func.apply(obj,[value1,value2]); func.bind(obj,value1,value2)(); func.bind(obj)(value1,value2); 动态改变 this 的指向 obj;
独立调用和方法调用
全局环境中,this 默认指向到 window;
函数独立调用(不论这个函数在哪调用),this 默认指向到 window;
当函数被作为对象的方法(对象的一个属性)调用时,this 指向该对象;
函数只有调用了之后才有 this 的概念,不然它只是代码而已,我们经常把函数传给一个变量,如果后面没加括号,就只是传了个函数,而不是执行结果;
<script type="text/javascript">
console.log(this === window);//true 全局环境中,this默认指向到window function abc(){
console.log(this === window);
}
abc();//true 函数abc独立调用(不论这个函数在哪调用),this默认指向到window function outer(){
function inner(){
console.log(this === window);
}
inner();
}
outer();//true 函数inner独立调用(不论这个函数在哪调用),this默认指向到window var o = {
m: function(){
return this;
},
n: function(){
function test(){
return this;
}
return test(); //函数test独立调用(不论这个函数在哪调用),this默认指向到window
}
};
console.log(o.m());//Object o{...} 当函数被作为对象的方法(对象的一个属性)运行时,this指向该对象
console.log(o.n());//Window{...} 函数test独立调用,this默认指向到window
</script>
当函数被作为对象的方法调用时,可以使用 this 访问自己所属的对象,所以它能从对象中取值或对对象进行修改;
this 到对象的指向发生在调用的时候,如果函数没有被调用,则不会更改对象的变量;
通过 this 可取得它们所属对象的上下文的方法称为公共方法;
<script type="text/javascript">
var o = {
a: 1,
m: function(){
return this;
},
n: function(){
this.a = 2;
//当函数被作为对象的方法调用时,可以使用this访问自己所属的对象,所以它能从对象中取值或对对象进行修改。
//this到对象的绑定发生在调用的时候。所以这里的函数 n 如果没有调用过,那么外部的 a 是不会被改变的
}
};
console.log(o.m().a);//1
o.n();
console.log(o.m().a);//2
</script>
如果想访问这个外部函数的 this 值,需要将 this 的值保存在一个变量里,内部函数就可以通过作用域链找到这个变量。
<script type="text/javascript">
var o = {
m: function(){
console.log(this); //{} 当函数被作为对象的方法(对象的一个属性)运行时,this指向该对象
},
n: function(){
console.log(this);//{} var that = this;
//如果想访问这个外部函数的this值,需要将this的值保存在一个变量里,内部函数就可以通过作用域链找到这个变量。 function test(){
console.log(this);//window
console.log(that);//{}
}
return test(); //函数test独立调用(不论这个函数在哪调用),this默认指向到window
}
};
o.m();
o.n();
</script>
构造函数调用
如果一个函数用 new 关键字调用,那么这个函数就是构造函数,并且背地里会创建一个连接到该函数的 prototype 的新对象,this 指向这个新对象;
如果构造函数没有形参,实例化的时候是可以不带()的;如 var a = Func; 或者 var a = Func(); 两种都可以;
同时我们在构造函数的时候有个约定(不是规范),首字母大写,以避免忘了写new关键字或者在普通函数前面加new;
new 关键字的作用就是执行一个构造函数,并返回一个对象实例。使用 new 命令,它后面的函数的函数调用和普通函数调用就不一样了,步骤如下:
- 创建一个空对象,作为将要返回的对象实例;
- 将空对象的原型 _proto_ 指向构造函数的 prototype 属性;
- 将构造函数内部的this关键字指向空对象;
- 执行构造函数内部的代码;
就是说 this 指向这个新对象,构造函数内所有针对 this 的操作,都会发生在这个新对象上;
<script type="text/javascript">
// 创建一个名为Person 的构造函数,它构造一个带有user 和age 的对象
var Person = function (user,age) {
this.user = user;
this.age = age;
}; // 构造一个Person 实例 ,并测试
var shane = new Person ('shane',25);
console.log(shane.user);//shane
console.log(shane.age);//25
</script>
javascript 中构造函数是不需要有返回值的,可以认为构造函数和普通函数之间的区别就是:构造函数没有 return 语句,普通函数可以有 return 语句;
构造函数使用 this 关键字定义变量和方法,当 this 遇到 return 的时候,会发生指向不明(调用结果不明)的问题:
- return 返回的不是一个对象,this 还是指向实例(新对象),调用结果也还是新对象;
- return 返回的是一个对象,this 就指向这个返回的对象,调用结果就是这个返回的对象;
- return 返回的是 null,this 还是指向实例,调用结果也还是新对象;
<script type="text/javascript">
var Person = function(){
this.user = 'shane';
return
}
var shane = new Person;
console.log(shane.user);//shane return没有返回值,this还是指向实例(新对象),调用结果也还是新对象; var Color = function(){
this.red = 'red';
return 'hello world';
}
var redColor = new Color;
console.log(redColor.red);//red return返回的是一个基本类型的字符串(原始值),this还是指向实例(新对象),调用结果也还是新对象; var Size = function(){
this.size = 'big';
return {};
}
var sizeBig = new Size;
console.log(sizeBig.size);//undefined return返回的是一个对象,this就指向这个返回的对象,调用结果就是这个返回的对象;
</script>
间接调用
通过 call()、apply()、bind() 方法把对象绑定到 this 上,叫做显式绑定。对于被调用的函数来说,叫做间接调用
- call、apply、bind三者的第一个参数都是this要指向的对象,
- bind 只是返回函数,还未调用,所以如果要执行还得在后面加个();call、apply 是立即执行函数;
- 三者后面都可以带参数,call 后面的参数用逗号隔开,apply 后面的参数以数组的形式传入;bind则可以在指定对象的时候传参,和 call 一样,以逗号隔开,也可以在执行的时候传参,写到后面的括号中;func.call(obj,value1,value2); func.apply(obj,[value1,value2]); func.bind(obj,value1,value2)(); func.bind(obj)(value1,value2);
<script type="text/javascript">
var add = function (a,b) {
return a+b;
}; var arr = [2,3]; var sum0 = add.apply(null, arr);//apply后面的参数以数组的形式传入
var sum1 = add.call(null,arr[0],arr[1]);//call后面的参数用逗号隔开
var sum2 = add.bind(null,arr[0],arr[1]);//bind后面的参数可以和call一样,用逗号隔开
var sum3 = add.bind(null); //bind调用的时候也可以在执行的时候传参 console.log(sum0);//5
console.log(sum1);//5
console.log(sum2);//function (a,b) {return a+b;}; bind只是返回函数,还未调用,
console.log(sum2());//5 所以bind调用的时候,如果要执行还得在后面加个()
console.log(sum3(arr[0],arr[1]));//5 bind调用的时候也可以在执行的时候传参
</script>
严格模式下
- 在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。 除非明确把函数添加到某个对象或者调用 apply() 或 call(),否则 this 值将是 undefined;
- 所以我们可以手动添加 window.函数(),将 this 指向 window;
<script type="text/javascript">
'use strict' //严格模式
function a(){
console.log (this);
}
a();//undefined
//在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window , this 值将是 undefined。
//除非明确把函数添加到某个对象或者调用 apply()或 call()使用对象冒充。 window.a();//window
//所以我们可以手动添加 window.函数(),来改变this指向window (function(){
console.log(this);//undefined
})(); setTimeout(function(){
console.log(this);//Window
},0);
//setTimeout是window的一个方法(属性),所以这里可以写成window.setTimeout
//所以不管是严格模式还是非严格模式,setTimeout里的this始终指向window
//但是有时候我们会发现setTimeout中的this也有其他情况,这只是误导,当然如果你不确定的话,直接console.log(this)一下,分分钟解决
</script>
javascript 函数的4种调用方式与 this(上下文)的指向的更多相关文章
- javascript函数的4种调用方式
在javascript中一共有4种函数调用模式,分别是:方法调用模式.函数调用模式.构造函数调用模式和apply(call)调用模式,这4种模式的主要差异在于如何初始化关键参数this. 方法调用模式 ...
- js函数的四种调用方式以及对应的this指向
一.函数调用,此时this是全局的也就是window 1 var c=function(){ 2 alert(this==window) 3 } 4 c()//true 二.方法调用 var myOb ...
- JavaScript 函数的4种调用方法
JavaScript 函数有 4 种调用方式. 每种方式的不同方式在于 this 的初始化. 作为一个函数调用 function myFunction(a, b) { return a * b; } ...
- JavaScript函数的4种调用方法详解
在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或其他描述性语言那样仅仅作为一个模块来使用.函数有四种调用模式,分别是:函数调用形式.方法调用形式.构造 ...
- JS高级. 06 缓存、分析解决递归斐波那契数列、jQuery缓存、沙箱、函数的四种调用方式、call和apply修改函数调用方法
缓存 cache 作用就是将一些常用的数据存储起来 提升性能 cdn //-----------------分析解决递归斐波那契数列<script> //定义一个缓存数组,存储已经计算出来 ...
- Javascript中函数的四种调用方式
一.Javascript中函数的几个基本知识点: 1.函数的名字只是一个指向函数的指针,所以即使在不同的执行环境,即不同对象调用这个函数,这个函数指向的仍然是同一个函数. 2.函数中有两个特殊的内部属 ...
- JavaScript 函数的两种声明方式
1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...
- javascript this 代表的上下文,JavaScript 函数的四种调用形式
JavaScript 是一种脚本语言,支持函数式编程.闭包.基于原型的继承等高级功能.其中JavaScript 中的 this 关键字,就是一个比较容易混乱的概念,在不同的场景下,this会化身不同的 ...
- 第十篇----------javascript函数的三种定义方式及区别
javascript定义函数有3种方式: //3种函数定义方式,前两种常用 /** * 1,function 语句式 * 形式:句子 * 名称:有名 * 性质:静态 * 解析时机:优先解析 * 作用域 ...
随机推荐
- Maven-07: 插件的自定义绑定
除了内置绑定以外,用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上,这种自定义绑定方式能让Maven项目在构建过程中执行更多更富特色的任务. 一个常见的例子是创建项目的源码jar包.内置的插 ...
- 【Python】 配置解析ConfigParser & 命令行参数解析optparser
ConfigParser ConfigParser包装了配置文件的读取和写入,使得python程序可以更加轻松操作配置文件了.这里的配置文件是指.ini的那种文件,基本格式如下 [section_a] ...
- c# 基于FTP协议的简易软件自动升级程序
最近在重写了一个老的产品条码扫描程序,客户端数越有30个,因为经常有更新,C/S维护非常不方便,所以做一个自动更新程序特别有必要. 在网上随便找了找自动更新的方案,大多使用VS的发布/更新功能,不太喜 ...
- 漫谈Java IO之 NIO那些事儿
前面一篇中已经介绍了基本IO的使用以及最简单的阻塞服务器的例子,本篇就来介绍下NIO的相关内容,前面的分享可以参考目录: 网络IO的基本知识与概念 普通IO以及BIO服务器 NIO的使用与服务器Hel ...
- 【Spring系列】自己手写一个 SpringMVC 框架
参考文章 一.了解SpringMVC运行流程及九大组件 1.SpringMVC的运行流程 1)用户发送请求至前端控制器DispatcherServlet 2)DispatcherServlet收到请求 ...
- 第二周作业(pta存在的问题)
***第一题 错题截图 错因分析:中英文符号用混 改正截图: 思路分析:(1)由输入格式可知,该程序需要用到scanf函数 (2)构建框架 (3)根据要求打入代码 **第二题(正确) 代码截图: 思路 ...
- C语言博客作业—指针
一.PTA实验作业 题目1: 求出数组中最大数和次最大数 1. 本题PTA提交列表 2. 设计思路 定义max表示范围数组中的最大数(初值设为a[0]),z表示找到的元素在数组中的位置: 定义指针*b ...
- 视图和URL配置
视图和URL配置 实验简介 上一章里我们介绍了如何创建一个Django项目并启动Django的开发服务器.本章你将学到用Django创建动态网页的基本知识. 同时,也教会大家怎么在本地机器上建立一个独 ...
- find命令之(-atime,-ctime,-mtime)
关于find命令,以拙见总结如下: >>>定义: find命令用来在指定目录下查找文件. 任何位于参数之前的字符串都将被视为欲查找的目录名.如果使用该命令时,不设置任何参数,则fin ...
- Flask 学习 十一 关注者
数据库关系 1.1多对多关系 添加第三张表(关联表),多对多关系可以分解成原表和关联表之间的两个一对多的关系 多对多仍然使用db.relationship()方法定义,但是secondary参数必须设 ...