函数的三种调用方式
- 全局函数
//全局函数 this指向window 因为fn全局函数是window的一个属性
function fn() {
console.log(this);//window
}
// window.fn();
fn();
- 对象方法
//对象的方法
var p = {
name: '张三',
age: 15,
sayHi() {
console.log(this);//p对象
console.log(this === p);//true
}
}
p.sayHi();
//将fn函数赋值给对象的sayHi
p.sayHi = fn;
p.sayHi();//此时执行fn中代码 this指向p
- 构造函数
//构造函数 new会做四件事 会将this指向new创建的对象
function Stu(name, age) {
this.name = name;
this.age = age;
}
var s = new Stu('李四', 18);
console.log(s);
//普通函数 相当于window.Stu()调用 this是window 给window动态加属性
var s1 = Stu('王五', 20);
console.log(s1);//没有返回值 结果undefined
console.log(name);//王五
console.log(age);//20
this谁调用 this指向谁
全局函数中 this指向window
对象方法中 this指向对象
构造函数中 this指向new创建的对象
定时器中 this默认指向window
- 函数的三种调用方式的共同点
this的指向是确定的无法修改的
函数的上下文调用
- 可以动态修改this的指向
- call()
语法函数名.call(修改this的指向,arg1,arg2...)
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
fn.call({ name: '张三' }, 10, 20);//修改后this指向这个对象
- apply()
语法函数名.apply(修改this的指向,[arg1,arg2...])
第二个参数是数组或伪数组 会自动遍历数组或伪数组的每一个元素给函数的形参赋值
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
fn.apply([1, 2], [30, 40]);//修改后this指向这个数组
-
bind()
语法函数名.apply(修改this的指向,arg1,arg2...)
bind()不会执行这个函数 而是会返回一个修改了this之后的新函数 然后再对这个新函数进行传参bind ()修改this的指向一般用于回调函数 如定时器
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
//bind与call不同的是 它不会执行这个函数
//而是会返回一个修改了this之后的函数
//然后在堆返回的函数传参
//bind一般用于回调函数中 如定时器
var fn2 = fn.bind([2, 22, 222]);
fn2(1, 11)
//定时器的this默认指向window 可以用bind修改this的指向
//匿名函数
setTimeout(function () {
console.log(this);
//回调函数中修改this 此时this执行对象
}.bind({}), 2000);
//表达式函数
var test = function () {
console.log(this);
}
setTimeout(test.bind([10, 20]), 1000);
- 上下文调用模式三种语法的异同点
- 相同点: 作用都是动态修改this的指向
- 不同点传参方式不一样
- 上下文调用模式三种方法 其实是属于Function构造函数的原型的 意味着js中所有的函数都能使用上下文调用修改this的指向
- 修改的this只能是引用类型
如果将this改为值类型 会自动转成基本包装类型
如果将this改为undefined和null 程序不会报错 也不会修改 this依旧指向window
如果没有传参 this依旧指向window
- 伪数组转数组
方法1 声明空数组 遍历伪数组 往空数组中追加
//伪数组转数组
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
//方法1
var arr =[];
for(var i=0;i<weiArr.length;i++){
arr.push(weiArr[i])
}
console.log(arr);
方法2 声明空数组 通过apply自动遍历伪数组
//方法2
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
var arr = [];
arr.push.apply(arr, weiArr)
console.log(arr);
方法3 通过数组的slice()方法
传入0 返回数组自身
通过数组的原型调用slice方法
通过call修改this的指向
//方法3
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
// var res = arr.slice(0);//会返回一个新数组 传入0 返回自身
// console.log(res);
var res = Array.prototype.slice.call(weiArr, 0);
console.log(res);
- 伪数组排序
//伪数组排序
var weiArr = {
0: 20,
1: 1,
2: 5,
3: 2,
4: 10,
length: 5
}
Array.prototype.sort.call(weiArr, function (a, b) {
return a - b;
})
console.log(weiArr);
- 求数组最大值
方法1 打擂台
//数组最大值
//方法1
var arr = [200, 50, 3, 60, 9, 66];
var max = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
console.log(max);
方法2 Math.max() 用apply将参数变成数组
//方法2
//Math.max([],[]...) 用apply将参数变成数组
console.log(Math.max.apply(Math,arr));
思考题: 数组调用toString结果与对象调用toString结果为什么不一样??
因为Array的原型中有toString Object的原型中也有toString
根据变量访问原型链成员的构造 如果数组调用toString会访问Array原型中的toString
//Array.prototype.toString() 底层调用的是 数组.join()
//Object.prototype.toString() 返回固定的字符串 [Object 数据类型]
console.log([].toString());//空字符串
console.log({}.toString());//[object Object]
console.log([1,2,3].toString());//1,2,3
console.log({name:'张三'}.toString());//[Object Object]
万能数据类型检测法
typeof
关键字检测null和array都会得到object 无法检测
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call([]));//[object Array]
借用构造函数继承
借用Person中的两行代码赋值给Stu函数 通过call修改this为Stu函数的this
只能继承this点出来的属性 如果是实例化点出来的属性无法继承
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('张三', 18);
console.log(p);
function Stu(name, age) {
// this.name = name;
// this.age = age;
// Person(name,age);
//借用构造函数继承 通过call修改this为Stu函数中的this
Person.call(this, name, age);
}
var s = new Stu('学生', 18)
console.log(s);