1、方法调用模式
当一个函数被保存在为对象的一个属性的时,我们称它为一个方法,当一个方法被调用时,this被绑定到该对象;
var name="全局name";
var a ={
name:"a的name",
newName:this.name,
b:function(){
console.log("你好");
console.log(this);//指向a {name: 'a的name', newName: '全局name', b: ƒ}
console.log("这里是:",this.name);//a的name
console.log(this.newName);//全局name
this.name="Hello"
var func = function(){
console.log(this);//当前this指向window
console.log(this.name);//全局name
};
func();//函数上下文是window,所以这是this 指向 window
}
}
a.b();//你好
func()这个函数执行的时候他的函数上下文为window。
一:通过赋值的方式改变this的指向
var a ={
b:function(){
console.log("你好");
let _this=this
_this.name="Hello"
var func = function(){
_this.name="HelloTwo"
console.log(_this.name);//js 就近原则。没有再向上查找 HelloTwo
console.log(_this);//当前this 指向a
};
func()
}
}
a.b();//你好
2.函数调用模式
当函数并非一个对象的属性时,那么它被当做一个函数来调用,this被绑定到全局对象(因为这个时候foo这个函数的上下文是window绑定的,this就指向window);
var object ={value:100};
object.geValue= function(){
console.log(this.value);//this 指向 object
var _this=this;//把object 对象赋值给变量_this
var fun =function(value){
this.value=value
console.log(this);//函数调用,this 指向函数上下文window
console.log(this.value);//12
console.log(_this.value)//100
}
fun(12);
return this.value
}
console.log(object.geValue());//没有设置return 返回undefined,设置了就返回 100
3.构造器中的this:值向新的函数
js中,我们通过new关键词来调用构造函数,此时this会绑定在该对象上
function start(name){
this.name=name
}
start("王牌");
console.log(this.name);//王牌 console.log(window.name) this 指向window
var starts= new start("王炸");
console.log(starts.name);//王炸 指向 starts
.bind()
用法
1. 用于绑定this
var obj = {
name: 'obj',
say: function () { // 这里的this指向obj
console.log(this.name); // obj
}
}
var obj2 = {
name: 'obj2'
}
obj.say(); // obj
obj.say.bind(obj2)(); // obj2
2. 用于绑定参数和多个参数
var obj = {
name: 'obj',
say: function (age) {
console.log(this.name + ' ' + age);
}
}
var obj2 = {
name: 'obj2'
}
obj.say(18); // obj 18
obj.say.bind(obj2, 20)(); // obj2 20
// 多个
var obj = {
name: 'obj',
say: function (age, event) {
console.log(this.name + ' ' + age + ' ' + event.target);
}
}
var obj2 = {
name: 'obj2'
}
document.body.addEventListener('click', obj.say.bind(obj2, 20)); // obj2 20 body
document.body.addEventListener('click', obj.say.bind(obj, 20)); // obj 20 body
document.body.addEventListener('click', obj.say.bind(obj, 20, event)); // obj 20 body
document.body.addEventListener('click', obj.say.bind(obj, event, 20)); // obj [object MouseEvent] 20
3. 用于绑定事件对象
var obj = {
name: 'obj',
say: function (event) { // 这里的this指向obj, event指向事件对象, 但是bind方法会将event放在参数列表的最后
console.log(event.target); // body
}
}
var obj2 = {
name: 'obj2'
}
document.body.addEventListener('click', obj.say); // body
document.body.addEventListener('click', obj.say.bind(obj2)); // body
document.body.addEventListener('click', obj.say.bind(obj)); // body
document.body.addEventListener('click', obj.say.bind(obj, event)); // body
.bind() 指向和call一样,都是改变this指向,但是不会立即执行,而是返回一个函数,等待调用。
function.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
说明:bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
另外,bind() 函数会创建一个新函数,新函数的 this 会被绑定到 bind() 的第一个参数。
bind() 除了this 还接受其他参数,bind() 返回的函数也接受参数,这两部分的参数都会按照顺序在函数调用时拼接起来。
bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。
call()
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
call(thisArg, arg1, arg2, ...)
会立即执行,bind() 方法接收完参数后会返回一个函数。call() 不会。
thisArg(可选的):在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
arg1, arg2, ...:指定的参数列表。
返回值:使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
说明:
call() 允许为不同的对象分配和调用属于一个对象的函数/方法。
call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
注意:call() 方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。
使用 call 方法调用父构造函数
function Food(name) {
this.name = name;
}
Food.prototype.getName = function() {
return this.name;
}
function Product(name, price) {
this.name = name;
this.price = price;
}
Product.prototype.getPrice = function() {
return this.price;
}
function FoodProduct(name, price) {
Food.call(this, name);
Product.call(this, name, price);
}
FoodProduct.prototype = Object.create(Product.prototype);
FoodProduct.prototype.constructor = FoodProduct;
var cheese = new FoodProduct('feta', 5);
console.log(cheese.getName()); // feta
console.log(cheese.getPrice()); // 5
构造函数 Product 属于 Food 的子类。
Product 有两个参数,name 和 price,Food 有一个参数 name
使用 call 方法,我们可以在 Food 的上下文中调用 Product,传递 Food 的 this 作为 Product 的 this。这让我们拥有了在 Food 中设置 name 的能力,通过继承 Product,我们可以重用 Product 的方法设置 price。
使用 call 方法调用匿名函数
var animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (var i = 0; i < animals.length; i++) {
(function(i) {//这是一个 IIFE,一个立即调用的函数表达式。使用匿名函数来保存 i 的值
this.print = function() {
console.log('#' + i + ' ' + this.species
+ ': ' + this.name);//这里的 this 指向全局对象,即 window。
}
this.print(); // 调用匿名函数。
}).call(animals[i], i); // 使用 call 方法来调用匿名函数。并且指定 this 的值为 animals[i]。i 作为参数传递给匿名函数。
}
call 方法调用函数并且指定上下文的 'this'
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' '); // this 的值在运行时绑定,这里的this指向obj
console.log(reply);
}
var obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj); // cats typically sleep between 12 and 16 hours
//obj作为greet函数的this指向,并且传入参数,执行greet函数,并且返回结果,这里的this指向obj
使用 call 方法调用函数并且不提供参数
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // sData value is Wisen.
使用 call 方法调用函数并且提供参数
var sData = 'Wisen';
function display(nm) {
console.log('sData value is %s ', nm, this.sData);
}
display.call(this, 'hello'); // sData value is hello Wisen
使用 call 方法调用函数并且提供另一个带有 call 方法的函数作为参数
function sayHello() {
console.log('Hello ' + this.name);
}
var person = {
name: '张三'
};
sayHello.call(person); // Hello 张三。this 指向 person 对象, 传递参数作为后续参数
使用场景
1.改变this指向
function fn1() {
console.log(this) // { a: 1 } 没有使用call方法之前this指向window
}
fn1.call({ a: 1 })
2.继承
function Parent() {
this.name = 'parent'
}
function Child() {
Parent.call(this) //this指向Child Child继承了Parent的属性,但是没有继承原型上的属性,所以Child.prototype.constructor = Child
this.type = 'child'
}
let child = new Child() //因为Child继承了Parent的属性,所以child.name = 'parent'
console.log(child) // Child {name: "parent", type: "child"}
3.借用方法
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
//Array.prototype.push.call是借用Array.prototype.push方法.
Array.prototype.push.call(arr1, ...arr2) //这里的this指向arr1,相当于arr1.push(...arr2),但是arr1的长度不会改变,因为call只是借用了push方法
console.log(arr1) // [1, 2, 3, 4, 5, 6]
4.调用函数
function fn2(a, b, c) {
console.log(a, b, c) // 1 2 3
}
fn2.call(null, 1, 2, 3) // 第一个参数为null或者undefined时,this指向window
5.延迟执行
function fn3() {
console.log(this) // { a: 1 }
}
setTimeout(fn3.call({ a: 1 }), 1000) //这里使用 call的好处是可以传参(立即执行)
如果使用apply,则需要将参数放在数组中,如下
setTimeout(fn3.apply({ a: 1 }, [1, 2, 3]), 1000);(不会立即执行)
// 不可以不是哟call。因为setTimeout的第一个参数必须是函数,而不是函数的返回值
6.函数柯里化
function fn4(a, b, c) {
return a + b + c
}
function curry(fn) {
let args = [].slice.call(arguments, 1) // [1, 2] [].slice.call(arguments, 1)相当于Array.prototype.slice.call(arguments, 1),arguments是一个类数组对象,没有slice方法,所以要借用Array.prototype.slice.call(arguments, 1)来调用slice方法
return function () { // 返回一个函数,这个函数中的arguments是一个类数组对象,没有slice方法,所以要借用Array.prototype.slice.call(arguments)来调用slice方法
let newArgs = args.concat([].slice.call(arguments)) // [1, 2].concat([3])相当于[1, 2, 3] args是一个数组,所以可以直接调用concat方法
return fn.apply(this, newArgs) // fn.apply(this, newArgs)相当于fn(1, 2, 3) apply方法的第二个参数是一个数组
}
}
let fn5 = curry(fn4, 1, 2)
console.log(fn5(3)) // 6
7.判断数据类型
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1) ;//返回类型的字符串,如Array,Object,Number .slice(8, -1)截取字符串,
}
console.log(getType([])) // Array
console.log(getType({})) // Object
console.log(getType(1)) // Number
console.log(getType('')) // String
console.log(getType(true)) // Boolean
console.log(getType(null)) // Null
console.log(getType(undefined)) // Undefined
console.log(getType(Symbol())) // Symbol
console.log(getType(function () { })) // Function
8.数组去重
Set是一种新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
跟call,apply一样,Set函数可以接受一个数组(或者具有iterable接口的其他数据结构)作为参数,用来初始化
let arr4 = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
let arr5 = Array.from(new Set(arr4))
console.log(arr5) // [1, 2, 3, 4, 5]
9.数组扁平化
let arr6 = [1, [2, [3, [4, 5]]], 6]
let arr7 = arr6.flat(Infinity);// Infinity表示无穷大,可以展开任意深度的嵌套数组,但是性能不好,可以使用递归来实现,或者使用正则,或者使用reduce,或者使用扩展运算符
console.log(arr7) // [1, 2, 3, 4, 5, 6]
10.数组中位数
let arr12 = [1, 2, 3, 4, 5]
let len = arr12.length
let mid = len % 2 === 0 ? (arr12[len / 2] + arr12[len / 2 - 1]) / 2 : arr12[(len - 1) / 2];//奇数个数时,中位数为中间的数,偶数个数时,中位数为中间两个数的平均值
console.log(mid) // 3
apply
apply和call的区别在于传参的方式不同, apply传参是数组, call传参是一个一个的传
数组中的最大差值
let arr13 = [1, 2, 3, 4, 5]
let max = Math.max.apply(null, arr13) // let max = Math.max(...arr13) 也可以 ,apply和...的区别:apply接收数组,...接收数组或者多个参数,apply的性能比...好,因为...会展开数组
let min = Math.min.apply(null, arr13)
let diff = max - min
console.log(diff) // 4
获取数组中的最大值和最小值
let max = Math.max(...arr3) 也可以 ,apply和...的区别:apply接收数组,...接收数组或者多个参数,apply的性能比...好,因为...会展开数组
let arr3 = [1, 2, 3, 4, 5]
let max = Math.max.apply(null, arr3)
let min = Math.min.apply(null, arr3)
console.log(max, min) // 5 1