阅读 154

作用域、变量提升、自由变量、this、闭包

作用域

作用域是某个变量合法使用的范围,分为:

  1. 全局作用域

  2. 函数作用域

  3. 块级作用域

变量提升

在一段JS脚本(即一个<script>标签中,或一个function中)执行前,要先解析代码(所以说JS是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文。先把代码中即将执行的函数声明都拿出来,再把变量声明拿出来。

函数声明优先级高于变量声明。

自由变量

  1. 一个变量在当前作用域没有被定义,但是被使用了

  2. 就会一层层向上级寻找,直到找到为止

  3. 如果找到全局作用域都没有找到,就报错 xxx is not defined

这种一层层的关系,就叫做作用域链

所有的自由变量都应该在函数定义的地方,向上级作用域寻找,不是在执行的地方。

this

this表示的是谁执行的你

this是执行上下文的一部分,this的取值是在函数执行时确定的,而不是函数定义时确定的。

var cyName = 'window-cy'
var obj = {
  cyName: 'obj-cy',
  printName: function(from){
    console.log(from + ': ' + this.cyName)
  }
}
obj.printName('obj') // obj-cy
obj.printName.call({cyName: 'call-cy'},'call') // call-cy
var p = obj.printName
p('window') // window-cy复制代码

var cyName = 'window-cy'换成let cyName = 'window-cy'的话,p('window')则打印undefined。因为p('window')此时的thiswindowvar声明的变量会自动挂载到window上,而let不会

this的使用场景

  1. 作为普通函数执行

  2. 作为对象属性执行

  3. 作为构造函数执行

  4. 箭头函数中执行

  5. 使用call apply bind调用

闭包

闭包closure指的那些引用了另一个函数作用域中变量的函数

闭包的使用场景

  1. 函数作为返回值返回

  2. 函数作为参数传递

function F1() {
    var a = 100
    return function () {
        console.log(a) // 100
    }
}
var f1 = F1()
var a = 200
f1()复制代码
function F1() {
    var a = 100
    return function () {
        console.log(a) // 100
    }
}
function F2(f1) {
    var a = 200
    f1()
}
var f1 = F1()
F2(f1)复制代码

实际开发中闭包的应用场景

隐藏数据,对外只提供API

function createCache(){
  const prefix = 'portal-'
  const data = {}
  return {
    set: function(key,value){
      data[prefix + key] = value
    },
    get: function(key){
      return data[prefix + key]
    }
  }
}
const c = createCache()
c.set('name','cy')
cy.get('name')复制代码

相关题目

var a = 10;
function b() {
  a = 100;
}
b();
console.log(a); // 100复制代码
// 变量提升
var a = 10;
function b() {
  a = 100;
  return;
  function a() {}//执行函数b之前,会先解析代码,把函数声明先拿出来,此时在b函数中,a就是funciton a。后续修改的就是函数b局部的变量a,与外部作用域中的a无关。 
}
b();
console.log(a);// 10复制代码
var a = 10;
if(true) {
  var a = 100; // var声明的变量没有块级作用域
}
console.log(a); // 100复制代码
var name = 'map';
function func() {
   console.log(this.name); // map
}

var object = {
   name: 'object',
   getNameFunc: function(fn) {
      fn && fn(); // 调用fn的是window,所以此时的this是window
      return () => {
       	return this.name;
      } // 箭头函数的this指向父级作用域,所以此时的this是object
   }
};

console.log(object.getNameFunc(func)()); // object
// 结果输出
// map
// object复制代码

手写call apply bind函数

利用函数作为对象属性调用的特性实现

var cyName = 'window-cy'
var obj = {
  cyName: 'obj-cy',
  printName: function (from) {
    console.log(from + ': ' + this.cyName)
  }
}
obj.printName.call({ cyName: 'call-cy' }, 'call')

Function.prototype.cyCall = function (o) {
  const symbol = Symbol()
  o[symbol] = this // 谁调用的cyCall,谁就是this
  // 取第一个参数以外的参数
  let args = Array.prototype.slice.call(arguments, 1) // arguments是类数组(含有length属性的对象),typeof arguments为object。Array.prototype.slice.call可以把类数组转化为数组
  o[symbol](...args)
  delete o[symbol]
}

obj.printName.cyCall({ cyName: 'cyCall-cy' }, 'cyCall')

Function.prototype.cyApply = function (o) {
  const symbol = Symbol()
  o[symbol] = this
  const args = Array.prototype.slice.call(arguments, 1)
  o[symbol](...args)
  delete o[symbol]
}

obj.printName.apply({ cyName: 'cyApply-cy' }, ['cyApply'])

Function.prototype.cyBind = function (o, ...args) {

  return function (...innerArgs) {
    const symbol = Symbol()
    o[symbol] = this
    o[symbol](...args, innerArgs.slice(args.length > 0 ? args.length - 1 : 0))
    delete o[symbol]
  }
}
obj.printName.bind({ cyName: 'cyBind-cy' }, 'cyBind')()


作者:iceychan
链接:https://juejin.cn/post/7015149221409996830


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐