调用模式:
函数调用模式。
就是通过函数来调用,规范写法为:function fn(){} fn(); 函数中this的指向->window。案例如下:
var name = `window`;
var obj = {
name: `obj`,
getName: function() {
function c () {
console.log(this.name); // 因为是函数调用模式,所以this指向window全局变量,所以输出为window
}
// 只看这个函数是怎么调用,不管函数是在哪声明的!
c ();
}
};
obj.getName();
方法调用模式。
就是通过对象的属性来调用,eg
let obj = {
say: function() {
console.log(this);
}
};
obj.say();
//函数中this的指向->当前对象。eg:
var age = 10;
var obj = {
age: 11,
getAge: function() {
console.log(this.age);// 11
}
};
obj.getAge();//因为是方法调用模式,this指向当前的对象obj。
构造函数模式
如果是作为构造函数来调用,那么this就是指向:new创建的新对象,这个是mdn的eg:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
// expected output: "Eagle"
这里顺便一提new,他就是
- 创建一个空的简单JavaScript对象(即{});
- 链接该对象(即设置该对象的构造函数)到另一个对象 ;
- 将步骤1新创建的对象作为this的上下文 ;
- 如果该函数没有返回对象,则返回this。
函数上下文(借用方法模式)。
上下文模式中this的指向和前三种模式不一样,它的this指向可以改变,而前三种模式是固定。函数上下文在我理解就是函数作用域。基本语法:apply和call 后面都是跟两个参数
apply和call第一个参数一样:表示使用那个对象来调用函数;apply第二个参数是:是一个数组或伪数组,数组的值做为函数的参数被传入;call第二个参数是:是基本数据类型(number string boolean);eg:
若要求一个数组中的最大值
常规写法
let arr = [9, 1, 4, 101, 7, 22, 8];
let maxNum = arr[0],
for(i=1; i <arr.length; i++) {
if(arr[i] > maxNum) {
maxNum = arr[i];
}
}
console.log(maxNum);
使用上下文调用模式(apply)
let arr = [9, 1, 4, 101, 7, 22, 8];
let maxNum=Math.max.apply(window,arr);//Math.max是window中的排序方法我们可以通过apply借用window中Math.max方法来对数组进行排序。
console.log(maxNum);
当一个函数被保存为对象的一个方法时,如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用,此时的this被绑定到这个对象。也就是call的方法
let a = 1
let obj1 = {
a:2,
fn:function(){
console.log(this.a)
}
}
obj1.fn.call(obj1)//2
此时的this是指obj1这个对象,obj1.fn.call(obj1),由于没有参数,所以只要写出this是谁就可了。事实上谁调用这个函数,this就是谁。补充一下,DOM对象绑定事件也属于方法调用模式,因此它绑定的this就是事件源DOM对象。eg:
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
点击页面,依次输出:document和window对象
是因为点击页面监听click事件属于方法调用,this指向事件源DOM对象,即obj.fn.apply(obj),setTimeout内的函数属于回调函数,可以这么理解,f1.call(null,f2),所以this指向window。
函数调用模式:
普通调用
普通函数的调用,此时的this被绑定到window
最普通的调用
function fn1(){
console.log(this)//window
}
fn1()
函数嵌套
function fn1(){
function fn2(){
console.log(this)//window
}
fn2()
}
fn1()
这时候也是window
把函数赋值之后再调用
var a = 1
var obj1 = {
a:2,
fn:function(){
console.log(this.a)
}
}
var fn1 = obj1.fn
fn1()//1
obj1.fn是一个函数function(){console.log(this.a)},此时fn1就是不带任何修饰的函数调用,function(){console.log(this.a)}.call(undefined),按理说打印出来的 this 应该就是 undefined 了吧,但是浏览器里有一条规则:
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
因此上面的this绑定的就是window,它也被称为隐性绑定。
如果你希望打印出2,可以修改fn1()为fn1.call(obj1),显示地绑定this为obj1
回调函数
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。回调函数就是写给别人用的函数,是写个浏览器回头调用的,调用的时机是等到浏览器的ready state=4,也就是读取服务器相应完了才会用的。要注意写给别人的才是回调,写给自己的不是回调
eg:
let f1=()=>{}
let f2=(fn)=>{fn()}
f2(f1)
此时fq的地址就作为fw中的一个参数被调用,所以fq就是一个回调函数。
直接调用的f2则不是回调函数。若是这样
let f1=()=>{}
let f2=(fn)=>{}
f2(f1)
虽然此时f2里面没有写怎么调用f1,但f2接受了f1这个参数肯定是要用的,所以此时f1仍然是回调,此时f2对于f1的调用方法就是不管他,让他自生自灭,这也算是调用了。
再看会开始的那个eg
let a = 1
function f1(){
(function (){var a = 2})()
console.log(a)//1
}
f1()
其实它仍旧是最普通的函数调用,f1.call(undefined),this指向window,打印出的是全局的a。
借此,我们终于可以解释为什么setTimeout总是丢失this了,因为它也就是一个回调函数而已。回调函数就是会自动指向window而非当前对象,这也被称为回调函数的this丢失。
构造器调用模式:
new一个函数时,背地里会将创建一个连接到prototype成员的新对象,同时this会被绑定到那个新对象上
function Person(name,age){
// 这里的this都指向实例
this.name = name
this.age = age
this.sayAge = function(){
console.log(this.age)
}
}
var dot = new Person('Dot',2)
dot.sayAge()//2
网友评论