什么是函数?
是实现特定功能的 n 条语句的封装体,只有函数是可以执行的,其他类型的数据不能执行
使用函数的好处?
可以提高代码复用性,便于阅读交流
怎么定义函数?
1、 函数声明方式来定义函数:
function square(number) {
return number * number;
}
2、 函数表达式的方式来定义函数:
var square = function(number) { return number * number; };
怎么调用执行函数?
1、 直接调用 :fn()
2、 通过对象调用: obj.test
3、 new调用: new Function()
4、 使用 call/apply 等方法调用:fn.call/apply(obj) 临时让fn成为 obj 的属性方法进行调用,如下面的例子
function showProps() {
this.name='lily';
}
var obj={};
showProps.call(obj);
console.log(obj);//{name: "lily"}
回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。
回调函数的使用场景?
1、异步编程
2、事件监听、处理
3、setTimeout(延时定时器)、setInterval(循环定时器)
在回调函数调用时 this 的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
调用回调函数的时候是在全局环境下,因此 this 指向的是window,所以sum的值是赋值给windows的,看下面例子:
//--------------直接调用函数时 this 指向--------------
function f(str) {
this.name=str;
}
f('ljy'); //直接调用相当于在 window全局对象上调用 f 方法,this 指向 window ,name是全局变量
console.log(window.name);//'ljy'
console.log(name);//'ljy'
//--------------调用对象中的属性方法时 this 指向--------------
var obj1={
sum:0,
add:function (num1,num2) {
this.sum = num1 + num2;
}
}
obj1.add(1,1);//直接调用时 add 方法是作用于 obj1 这个对象上的,this.sum 指obj1 的sum属性
console.log(obj1.sum);//2
console.log(window.sum);//undefined
//--------------回调函数中 this 指向--------------
/*在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
* 调用回调函数的时候是在全局环境下,因此this指向的是window,所以sum的值是赋值给windows的*/
function add(a,b,callback) {
callback(a,b);
}
add(2,3,obj1.add);
console.log(obj1.sum);//2
console.log(window.sum); //5
函数的 this
在调用时绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。
一般情况下, 任何函数本质上都是通过 this
对象 调用的,this
指向的是调用函数的对象,没有直接指定的对象默认是 window
,下面分析函数调用的几种方式:
注意: 使用 call 方法调用函数时,this 指向的是 call 方法中的参数对象。
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
this指向问题总结:
// this指向问题总结:
// 1、普通函数直接调用:this 指向的对象是window
function f() {
console.log('普通函数的this:'+this)
}
f()//window
// 2、对象的方法:this 指向的是对象obj
var thisObj
var obj={
method:function () {
thisObj=this
console.log('对象方法的this:'+this)
}
}
obj.method()
console.log(obj===thisObj)//true
// 3、构造函数:this 指向的是构造函数创建的实例对象
var that;
function Person() {
that=this
}
var person = new Person()
console.log(that===person)//true
// 4、绑定事件函数:this指向的是函数调用者btn 这个按钮元素
var btn = document.querySelector('button')
btn.onclick=function () {
console.log('绑定事件函数的this:'+this)//绑定事件函数的this:[object HTMLButtonElement]
}
// 5、 定时器函数:this指向window
setTimeout(function () {
console.log('定时器的this:'+this)//[object Window]
},2000)
// 6、立即调用函数:window
;(function () {
console.log('立即调用函数的this:'+this)//[object Window]
})()
注: 上面例子 5 中,如果没有特殊指向,setInterval
和 setTimeout
的回调函数中 this
的指向都是 window
。这是因为 JS 的 定时器方法
是定义在 window
下的。
如果需要改变 this 指向,JavaScript提供了一些函数方法来处理改变 this指向问题:常用的有 call apply bind ,关于改变 this 指向及 call apply bind 三种方法的使用记录在 这篇文章中 。
!!!总结: 由于 this 在函数调用时才确定是会动态改变的,与其调用方式和运行的环境有关系,与其函数定义的位置,所处的作用域没有直接关系,看下面例子加深理解。区别以下几种不同的调用方式 this 的结果:
let callObj={
name:'call改变的this'
}
let name = 'window'
let obj={
name:'Obj',
show:function (str) {
console.log(str+this.name)
function f(){
console.log('f函数直接调用指向的this:'+this.name)
}
f()//f 始终为直接调用,this 都是指向window,与其所在的作用域执行上下文无关
}
}
//以对象的方法调用
obj.show('以对象的方法调用时this:')
//全局变量 fn 引用该函数,在 window下直接调用
let fn = obj.show
fn('全局变量 fn 引用该函数,在 window下直接调用:')
// 使用 call 方法改变this 指向
fn.call(callObj,'使用 call 方法改变this :')
输出结果:
箭头函数回顾:JavaScript-- ES6 箭头函数
箭头函数没有 this,只会从自己的作用域链上继承上一层作用域的 this,所以箭头函数也不能使用 call apply bind 方法来改变 this 指向,第一个参数会忽略无效。
两道题对 this
指向问题进行巩固:
题目参考文章:https://segmentfault.com/a/1190000010981003
var name = 'window'
var person1 = {
name: 'person1',
show1: function () {
console.log(this.name)
},
show2: () => console.log(this.name),
show3: function () {
return function () {
console.log(this.name)
}
},
show4: function () {
return () => console.log(this.name)
}
}
var person2 = { name: 'person2' }
//普通函数:谁调用该函数 this 就指向谁
person1.show1()//person1
person1.show1.call(person2)//person2
//箭头函数:this继承其作用域链中上一层作用域的 this,即直接包含它的那个函数或函数表达式中的 this,call等修改this方法无效
person1.show2()//window
person1.show2.call(person2)//window
person1.show3()()//window
person1.show3().call(person2)//person2
person1.show3.call(person2)()//window
person1.show4()()//person1
person1.show4().call(person2)//person1
person1.show4.call(person2)()//person2
第二道:
var name = 'window'
function Person (name) {
this.name = name;
this.show1 = function () {
console.log(this.name)
}
this.show2 = () => console.log(this.name)
this.show3 = function () {
return function () {
console.log(this.name)
}
}
this.show4 = function () {
return () => console.log(this.name)
}
}
var personA = new Person('personA')
var personB = new Person('personB')
personA.show1()//personA
personA.show1.call(personB)//personB
personA.show2()//personA
personA.show2.call(personB)//personA
personA.show3()()//window
personA.show3().call(personB)//personB
personA.show3.call(personB)()//window
personA.show4()()//personA
personA.show4().call(personB)//personA
personA.show4.call(personB)()//personB