作用域
作用域是某个变量合法使用的范围,分为:
- 全局作用域
- 函数作用域
- 块级作用域
变量提升
在一段JS脚本(即一个<script>标签中,或一个function中)执行前,要先解析代码(所以说JS是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文。先把代码中即将执行的函数声明都拿出来,再把变量声明拿出来。
函数声明优先级高于变量声明。
自由变量
- 一个变量在当前作用域没有被定义,但是被使用了
- 就会一层层向上级寻找,直到找到为止
- 如果找到全局作用域都没有找到,就报错 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')此时的this是window,var声明的变量会自动挂载到window上,而let不会。
this的使用场景
- 作为普通函数执行
- 作为对象属性执行
- 作为构造函数执行
- 箭头函数中执行
- 使用
callapplybind调用
闭包
闭包closure指的是那些引用了另一个函数作用域中变量的函数。
闭包的使用场景
- 函数作为返回值返回
- 函数作为参数传递
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
- 实现防抖debounce、节流throttle
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.flat())
delete o[symbol]
}
obj.printName.cyApply({ cyName: 'cyApply-cy' }, ['cyApply'])
Function.prototype.cyBind = function (o, ...args) {
return (...innerArgs) => {
const symbol = Symbol()
o[symbol] = this
o[symbol](...args, ...innerArgs)
delete o[symbol]
}
}
obj.printName.cyBind({ cyName: 'cyBind-cy' }, 'cyBind')()