1.This指向问题
1、 this 全局中的this
console.log(this);
2、函数中的this
严格模式指向 undefined,非严格模式指向window
function fn(){
console.log(this);
}
fn();
3、普通回调函数中的this
严格模式指向 undefined,非严格模式指向window
function fn(f) {
f()
}
function fn1() {
console.log(this)
}
fn(fn1);
4、arguments回调函数
this指向回调当前函数的上下文环境中arguments对象
function fn(){
arguments[0]();
}
function fn1(){
console.log(this)
}
fn(fn1);
5、部分方法中回调函数,setTimeOut,setInterval,Promise
严格模式指向 undefined,非严格模式指向window
setTimeout(function(){
console.log(this)
},2000)
new Promise(function(resolve,reject){
console.log(this);
resolve();
}).then(function(){
console.log(this)
})
6、部分包括thisArg的回调方法,forEach,map,filter,find,findIndex,flatMapsome、every、findLast,findLastIndex
如果没有给入thisArg参数,this指向window或者undefined
如果给入thisArg参数,this指向这个thisArg参数
var arr=[1,2,3];
arr.forEach(function(){
console.log(this)
},{a:1})
console.log(arr)
7、事件回调函数
在事件回调函数或者on事件中,this指向被侦听的对象
document.addEventListener("click",clickHandler);
function clickHandler(e){
console.log(this)
}
document.onclick=function(){
console.log(this)
}
8、对象中的this
对象中this指向
属性中this指向对象外上下文环境中this指向
对象属性:function 所创建的方法,函数中this指向当前对象(谁调用这个方法,this指向谁)
对象属性(){} ES6方法,函数中this指向当前对象(谁调用这个方法,this指向谁)
对象中属性使用箭头函数,设置方法,this指向对象外上下文环境中的this指向
var obj={
a:1,
b:this.a,
c:function(){
console.log(this)
},
d(){
console.log(this);
},
e:()=>{
console.log(this)
}
}
obj.c();
var o=Object.create(obj);
o.c();
9、箭头函数
箭头函数中this指向箭头函数外上下文环境中的this指向
var fn=()=>{
console.log(this)
}
document.addEventListener("click",fn);
10、call、apply、bind
call和apply在使用时,非严格模式下,如果不是对象,数值、布尔值、字符都会被
转换为对象类型并且让this指向这个对象类型,如果传入undefined或者null,this
将会指向window
严格模式下,传入什么this指向什么
bind
11、ES6面向对象
构造函数中this指向实例化的对象
实例化方法中的this,谁调用指向谁
静态方法中this指向该类或者构造函数(禁止使用this)
实例化属性上this指向当前实例化对象
静态属性中this指向当前类或者构造函数(禁止使用this)
var b=3;
class Box{
b=1;
a=this.b
static b=5;
static c=this.b;
constructor(){
console.log(this)
}
play(){
console.log(this)
}
static run(){
console.log(this)
}
}
class Ball extends Box{
}
var b=new Ball();
b.play();
console.log(Ball.c)
12、原型中this指向
如果这个函数使用new实例化,这个是构造函数,所以this指向实例化的对象
如果这个函数直接执行,this指向window或者undefined
原型属性中使用this,this指向当前实例化对象
原型方法中this,指向当前实例化对象
原型方法中使用箭头函数,this依照箭头函数的指向
函数的方法中this执行当前函数,等同于ES6中静态方法,this禁止使用,指向这个类或者构造函数
var b=1;
function Box(){
console.log(this);
}
Box.prototype.b=100;
Box.prototype.a=this.b;
Box.prototype.play=function(){
console.log(this);
}
Box.prototype.run=()=>{
console.log(this)
}
Box.play=function(){
console.log(this)
}
var b= new Box();
console.log(b)
b.run();
2. 强行改变 this 指向
强行改变 this 指向
+ 不管你本身指向哪里, 我让你指向谁你就得指向谁
=> 统一语法: 函数.xxx()
=> 用来改变 this 指向的
+ 三个改变 this 指向的方法
1. call()
2. apply()
3. bind()
call
+ 语法:
=> 函数名.call()
=> 对象名.函数名.call()
+ 参数:
=> 第一个参数: 该函数本次调用时函数内的 this 指向
=> 第二个参数开始: 依次是该函数的每一个实参
+ 特点: 立即调用函数
apply
+ 语法:
=> 函数名.apply()
=> 对象名.函数名.apply()
+ 参数:
=> 第一个参数: 该函数本次调用时函数内的 this 指向
=> 第二个参数: 是一个数组或者伪数组数据类型都行, 内部的每一个数据依次是该函数的实参
+ 特点: 立即调用函数
+ 特殊作用: 改变函数传递参数的方式
bind
+ 语法:
=> 函数名.bind()
=> 对象名.函数名.bind()
+ 参数:
=> 第一个参数: 该函数本次调用时函数内的 this 指向
=> 第二个参数开始: 依次是该函数的每一个实参
+ 特点:
=> 不会立即调用函数, 而是返回一个新的函数 !!!!
=> 一个和原先一模一样的函数, 只是被锁定了 this
+ 特殊作用:
=> 修改一些不需要立即执行的函数的 this 指向
=> 例如: 事件处理函数, 定时器处理函数
例:
var obj = {
a: function () {
setTimeout(handler.bind(obj),2000);
function handler(){
console.log(this)
}
}
}
obj.a();
bind给函数内绑定一个obj并且返回一个新函数,2000毫秒后调用这个新函数
setTimeout(handler.bind(obj),2000);
例:
Function.prototype.bind1=function(thisArg,...arg){
var f=this;
return function(...arg1){
return f.apply(thisArg,[...arg,...arg1]);
}
}
3.setter和getter
1.set 属性名(value){
存储用的内部属性名=value;
。。。。当设置这个属性后随之需要的操作
}
Note:每个setter设置一个属性时,必须有一个参数value,
并且,我们需要用一个外部变量来接收这个参数,用于保存。因此setter写法基本固定
2.get 属性名(){
。。。当获取这个属性时需要操作的内容
return 内部存储的这个属性;
}
Note:每个getter是获取一个属性,因此,必须有一个return返回内部存储的值
3.个人:
set中有且仅有一个参数 而且是必填参数
get中不能使用参数 必须使用return返回一个值
两者方法名完全相同 (下面是a)
使用set和get时必须要使用与之相对应的临时属性 (下面是_a)
原因:set本身是不能存储值的 所以要给临时变量来存储值
例:
var obj={
_a:1,
set a(value){
console.log(value)
this._a=value;
},
get a(){
return this._a;
}
}
obj.a=10;
console.log(obj.a);
例:
var obj={
_a:1,
get a(){
return this._a;
}
}
注意setter和getter设置的属性一般是成对出现,对应的相应属性。
如果仅出现get没有出现set,表示该属性只读,不可写值。 只读不能设置属性
如果仅出现set,没有使用get,表示该属性只写,不能获取,
删除getter或setter的唯一方法是:delete object[name]。delete可以删除一些常见的属性,getters和setters。
4.闭包
什么是闭包:
1.闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
为什么要闭包:
1.出于种种原因,我们有时候需要得到函数内的局部变量值。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
闭包的作用:
1. 在外部访问函数内部的变量
2. 让函数内的局部变量可以一直保存下去
3. 模块化私有属性和公共属性
闭包的原理:
全局变量生存周期是永久,局部变量生存周期随着函数的调用介绍而销毁。
闭包就是 在函数中定义且成为该函数内部返回的函数的自由变量 的变量,该变量不会随着外部函数调用结束而销毁。
(注:不光是变量,函数内声明的函数也可以形成闭包)
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。
闭包的优点:
1.希望一个变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在
闭包的缺点:
1.占用内层空间 大量使用闭包会造成 栈溢出
2.会造成内存泄漏
如何优化闭包
由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁:
将闭包函数赋值为null 可以销毁闭包
闭包的例子:
function fn(){
return function(){
console.log("a")
}
}
var f=fn();
f();
5.柯理化和反柯理化
1.柯理化
函数柯理化
+ 闭包的一种高级应用
+ 意义: 把参数分开, 把不经常变换的参数锁死(以闭包的形式), 我们只需要在调用的时候传递会变化的参数即可
例:柯理化更方便的求和 (简单 简洁 别人看不懂 柯理化例子背下来)
function currying(fn){
var arr=[];
return function(){
if(arguments.length>0){
arr=arr.concat.apply(arr,arguments);
}else{
return fn.apply(null,arr);
}
}
}
var getSum=currying(function(){
return [].reduce.call(arguments,(v,t)=>v+t);
})
getSum(1,2,3);
getSum(4,5);
var s=getSum();
console.log(s)
2.反柯理化
在JavaScript中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点。
可以通过反柯里化(uncurrying)函数实现,让一个对象去借用一个原本不属于他的方法。
例:
反柯里化函数
function uncurrying(fn) {
return function () {
return Function.prototype.call.apply(fn, arguments);
}
};
var push=uncurrying([].push);
var splice=uncurrying([].splice);
function fns(fn){
var len= push(arguments,4,5,6);
console.log(len,arguments)
splice(arguments,1,1,5);
console.log(arguments);
}
fns(1,2,3);