JS三部曲,变量提拔,this与作用域,闭包

这篇文章总结下之前看的文章和本身在工作中碰到的坑,纯手写的,有什么写的不对的请多多提出修改哈

变量提拔

作甚变量提拔?js内里的变量在声明之前就能够运用,因为该声明已被提拔至该作用域(函数或全局)的顶部
直接上代码

function fn1(){
    console.log(a)
    var a=10;
    function a(){};
    console.log(a)
}
fn1();
//var和function都邑变量提拔,优先级function>var,上面可邃晓为
function fn1(){
    var a;
    function a(){};
    console.log(a);//a函数
    a=10;
    console.log(a);//10
}

这时刻我们加上一个参数来比较

function fn1(a){
    console.log(a);
    var a=20;
    function a(){};
    console.log(a);
}
fn2(10);//打印出函数a
//变量提拔有个优先级:函数a>参数>变量,上面可邃晓为
function fn2(a){//a=10
    var a;
    //a被参数的a赋值为10或许邃晓为后声明的掩盖不了前面已赋值的
    function a(){};//变量a变成函数a
    console.log(a);//函数a
    a=20;//a从新赋值为20
    console.log(a);//20
}

作用域

作用域有全局作用域和函数作用域,我的邃晓实在就是变量(标识符)作用域,当实行一段代码时会在地点作用域剖析变量和函数,该作用域变量会掩盖外围的作用域,该作用域找不到的标识符会沿着作用域链向上延长查找,找不到就报错;

var a = 10;
!function(){
    console.log(a);
    var a= 20;
    console.log(a);
}();
console.log(a);
//上面邃晓为
var a = 10;//全局作用域
!function(){
    var a;//函数作用域
    console.log(a);//undefined,a向上检察-》函数作用域-》全局作用域
    a= 20;
    console.log(a);//20
}();
console.log(a);//10,查找到全局作用域,函数作用域不可见

再来看一个例子

console.log(b)
if('b' in window){
    var b=10;
    console.log(b);
};
console.log(b);
//ES3,ES5中if..else,with,while,for,switch等等是没有作用域的,
//只要全局作用域和函数作用域,以下
var b;
console.log(b);//undefined
if('b' in window){
    b =10;
    console.log(b);//10
};
console.log(b);//10

闭包

每一个人都有差别邃晓,我的邃晓是闭包就是让函数闭不了包,外部变量的值被缓存,内部变量可接见外部变量,也能够说是外部变量可接见内部变量,用法差别说法就差别,闭包在一些简朴例子上能够替代new实例化的开支,用自实行函数先把该实行的实行完

var Fn =(function(){
    var obj = {};
    obj.id=10;
    return {
        obj:obj
    };
})();

来看一个最经常使用的淘宝tab栏切换例子

<ul>
    <li>素颜</li>
    <li>断桥残雪</li>
    <li>最好歌手</li>
</ul>

var tab = document.querySelectorAll('ul>li');
for(var a=0;a<tab.length;a++){
    tab[a].onclick=function(){
    console.log(a);
    }
};
//当点击tab每一项时刻,我们会很惊异地发明打印出来的都是tab.length,
//因为for轮回的时刻a不会进去函数内里,等轮回完毕后,当你点击都已变成
//tab.length,我们须要把a缓存起来

第一种

for(var a=0;a<tab.length;a++){
    tab[a].index=a;
    tab[a].onclick=function(){
        console.log(this.index);
    }
};
console.log(a);//tab.length,这个下面第三种要领会说
//这类是传统做法,把一个自定义属性绑定在每一个dom节点li上,增大dom的开支

第二种

for(var a=0;a<tab.length;a++){
    !function(a){
        tab[a].onclick=function(){
            console.log(a);
        }
    }(a);
};
//或许
for(var a=0;a<tab.length;a++){
    tab[a].onclick=(function(a){
        return function(){
            console.log(a);
        }
    })(a);
};
console.log(a);//tab.length
//这类是闭包写法,运用自实行函数缓存起每一个a,固然闭包也会有内存走漏,
//机能等别的问题,用的恰当照样能够的

第三种

for(let a=0;a<tab.length;a++){
    tab[a].onclick=function(){
        console.log(a);
    };
};
console.log(a);//a is not defined
//这是es6的写法,一个let搞定一切问题,原生的要领,引荐运用;
//人人发明第二个a打印'a is not defined',
//然则第一种和第二种要领就能够接见到a为tab.length,这有能够致使变量
//走漏或许争执,let就很好的处理了这个问题,那是因为es6新增
//了块级作用域,外部接见不到for的作用域

this

this指向实行时地点的作用域,平常为window和函数,node环境为global, 来看个例子就邃晓了

var id=10;
var obj  = {
    id:100,
    show:function(){
        console.log(this.id);
        console.log(this.name);
    }
}
var b = obj.show;
b();
obj.show();

//obj.show先保留起来后在挪用时,这时刻是直接挪用一个函数b,函数的this
//指向window,注重window.name是window默许就有的为空,效果就是10 ''
//obj.show()体式格局是直接挪用,this对象指向obj,name为undefined,
//效果就是100 undefined

this中的bind,call,apply

bind,call,apply能够转变当前函数的作用域,bind不一定要马上实行函数,call,apply必需马上实行函数 来看一个组织函数继承的例子

var Father = function(){
    this.name='张三';
    this.age=50;
    //以下三种都能够
    var son = Son.bind(this);//指向的对象
    son('葫芦娃',10);//间接挪用,也能够直接挪用
    //Son.apply(this,['葫芦娃',10]);//第一个参数是指向的对象,第二个参数是数组
    //Son.call(this,'葫芦娃',10);//第一个参数是指向的对象,背面离开写
}
Father.prototype.show=function(){
    console.log('爸爸叫'+this.name);
}
var Son = function(name1,age1){
    this.name1=name1;
    this.age1=age1;
}
var father = new Father;//经由过程转变Son组织函数的this指向为Father
console.log(father);//{name: "张三", age: 50, name1: "葫芦娃", age1: 10}

来看个bind的例子

var obj = {};
obj.show = function () {
    function _show() {
        console.log(this)
    };
    return _show.bind(obj);
}();
obj.show();

// 打印obj对象,因为先声明赋值了,自实行函数后,函数_show的this
//指向被bind要领改成obj

var obj = {
    show: function () {
        function _show() {
            console.log(this)
        };
        return _show.bind(obj);
    }()
  };
  obj.show();
  
 //打印window对象,没事先声明赋值,这里obj变量提拔,自实行函数后,
 //bind内里的obj为undefined,this为undefined的默许指向window

箭头函数:默许指向地点的宿主对象,也就是上一级对象,而不是实行时的对象,基于这个this指向上一级的特殊性,我们在某些情况下就不须要缓存this的值,直接运用;

var obj = {
    id:100,
    show:function(){
        (()=>{
            console.log(this)
        })();
        setTimeout(()=>{
            console.log(this)
        },1);
    },
    show1:()=>{
        console.log(this);
    }
};
var obj1 = obj.show;.
obj1();//window,箭头函数上一层是个一般函数,一般函数this指向window
obj.show();//obj,箭头函数上一层作用域的this指向obj
obj.show1();//window,this指向上一级即window

箭头函数与一般函数的夹杂嵌套

var obj = {
    show:function(){
        setTimeout(fn);
        function fn(){
            console.log(this);
            setTimeout(()=>{
                    console.log(this);
                    setTimeout(()=>{
                         console.log(this);
                    })
            })
    };
    },
    show1:function(){
        setTimeout(fn.bind(obj));
        function fn(){
            console.log(this);
            setTimeout(()=>{
                console.log(this);
                setTimeout(()=>{
                    console.log(this);
                })
            })
        };
    }
};
obj.show();//都是window,最表面的定时器是一般函数,一般函数this指向window,每一个箭头函数this指向上一层
obj.show1();//都是obj,最表面的定时器this指向被转变成obj,每一个箭头函数this指向上一层

综合题

第一道

var a = 1;
!function(){
    var a = 10;
    function fn(){
        this.a += this.a;
        a+=a;
        console.log(this.a);
        console.log(a);
    };
    var obj = {
        a:5,
        show:fn
    };
    obj.show();
    var obj1 = obj.show;
    obj1();
}();
//这里最主要是考核this指向,客岁口试时刻做到的笔试题,本身加以改进
//'obj.show()'直接挪用,此时函数fn的this指向obj,
//this.a就是5,this.a累加后就是10,依据就近准绳,变量a会
//沿着作用域链向上查找,找到上一层的10就住手了,
//a累加后就是20
//'var obj1 = obj.show;'这步保留起一般函数,一般函数被挪用时
//this指向就是window//,this.a===window.a就是1,累加后就是2,变量a依旧
//会沿着作用域链向上查找,找到上一层的是20,因为上面已被累加了一次,
//这是一个坑,很轻易遗忘,高低两次挪用是会相互影响的,a=20在累加就是40

//答案为:
//obj.show();//10 20
//var obj1 = obj.show;
//obj1();//2 40

第二道(闭包的典范问题,原题送上)

function fun(n,o) {
    console.log(o);
    return {
        fun:function(m){
          return fun(m,n);
        }
    };
};
//写出a,b,c的运转效果
//var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
//var b = fun(0).fun(1).fun(2).fun(3);
//var c = fun(0).fun(1); c.fun(2); c.fun(3);
//这里就是考核闭包的层层嵌套与多次回调,之前口试碰到的,
//当时做的时刻有点慌张,不过背面本身运转后发明做的照样准确的
//var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
//第一个a保留fun(0)的运转效果即return里的对象,因为o没传参数,
//打印undefined,参数n=0被保留在当前作用域中,不会被烧毁,
//供当前作用域链及以下运用,这是闭包的精华,背面回调函数传参也是云云,
//这个a.fun(1)就是挪用fun(0)的效果,传参m=1,实行返回fun(m=1,n=0)
//在实行回调fun(n=1,o=0),console.log(o)就是0,背面的a.fun(2)和
//a.fun(3)也是云云,打印都是0;

//var b = fun(0).fun(1).fun(2).fun(3);
//fun(0).fun(1)这步实在就是前面说的,
//fun(0).fun(1)的返回就是return的对象,.fun(2)在挪用改对象而且
//传参m=2,返回fum(m=2,n=1)在实行回调fun(n=2,o=1)
//打印console.log(o)就是1,而且返回return对象,
//在.fun(3)在挪用改对象而且传参m=3,返回fum(m=3,n=2)
//在实行回调fun(n=3,o=2)打印console.log(o)就是2

//var c = fun(0).fun(1); c.fun(2); c.fun(3);
//前面两个邃晓了,这个也不会有问题,就不多做诠释了

//var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined 0 0 0
//var b = fun(0).fun(1).fun(2).fun(3);//undefined 0 1 2
//var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined 0 1 1

写到这里终究完毕啦,js内里另有许多奇淫技能,每次看一篇好文或许一本书都邑被新的视角打击到,火线高能,还需继承踩坑,有什么须要交换斧正的请留言呀!
也能够加微信议论哦!
《JS三部曲,变量提拔,this与作用域,闭包》

    原文作者:繁华落幕
    原文地址: https://segmentfault.com/a/1190000013027852
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞