一.作用域链
1.通过一个例子
let a='global'
console.log(a);//'global'
function course(){
let b='js'
console.log(b);//'js'
session()
function session(){
let c=this
console.log(c);//Window
teacher()//函数提升
function teacher(){
let d='steven'
console.log(d);//'steven'
console.log('test1',b);//'js'
}
}
}
course()
2.改造一下这个例子
let a='global'
console.log(a);//'global'
function course(){
let b='js'
console.log(b);//'js'
session()
teacher()//报错
function session(){
let c=this
console.log(c);//Window
// teacher()
//函数提升--在作用域内提升(超出了当前作用域,所以报错)
function teacher(){
let d='steven'
console.log(d);
console.log('test1',b);
}
}
}
course()
3.继续改造这个例子(let 和var能否提升变量)
let a='global'
console.log(a);//'global'
function course(){
let b='js'
console.log(b);//'js'
session()
function session(){
let c=this
console.log(c);//Window
teacher()
//2.函数提升-作用域之内
function teacher(){
//2.1 let不支持提升
//2.2 变量通过var支持提升,变量的声明可以提升
//相当于执行了这样一个操作: var e=underfined(提升)
console.log('e',e);//undefined
let d='steven'
console.log(d);
//e='tom'
var e='tom'
console.log('test1',b);//'js' //3.作用域向上查找,向下传递
}
}
}
course()
4.提升优先级
//提升优先级
console.log('yunyin',yunyin);
function yunyin(){
this.course='js'
}
yunyin='course'
//变量优先,函数需要变量,所以变量最终会覆盖函数
//块级作用域
if(true){
let e=11
var f=222
}
console.log(f);
// console.log(e);
//1.对于作用域链我们可以直接通过创建态来定位作用域链 --静态创建
//2.手动取消全局
5.函数提升-作用域之内
let a='global'
console.log(a);//'global'
function course(){
let b='js'
console.log(b);//'js'
session()
function session(){
let c=this
console.log(c);//Window
teacher()
//函数提升-作用域之内
function teacher(){
console.log(d);//报错
let d='steven'
console.log(d);
console.log('test1',b);
}
}
}
course()
6.this/上下文context
例子:我家门有条河,门前的河上有座桥,门前的河里有群鸭.
我家门前有条河,这河上有座桥,这河里有群鸭.
这指的就是我家门前那条河,也就是上下文context.
结论:this是在执行时动态读取上下文决定的,而不是创建时.
7.考察重点--各使用态的指针指向
(1)函数直接调用中,this指向的是window
==>全局上执行的环境=>函数表达式/匿名函数/嵌套函数
为什么呢?因为它是通过调用它的调用方的执行环境来决定的.
function foo(){
console.log(this)
}
foo() //window.foo()
(2)隐式绑定--this指带的是调用堆栈的上一级=>对象/数组等引用关系逻辑
function fn(){
console.log('隐式绑定',this.a);//1
}
let obj={
a:1,
fn
}
obj.fn=fn
obj.fn()
面试题:
const foo={
bar:10,
fn:function(){
console.log(this.bar);//undefined
console.log(this);//Window
}
}
//取出
let fn1=foo.fn
//独立执行
fn1()
追问1:如何改变this指向?
const o1={
text:'o1',
fn:function(){
//直接使用上下文--传统派活
console.log('o1fn',this);
return this.text
}
}
const o2={
text:'o2',
fn:function(){
//呼叫领导执行,部门协作
return o1.fn()//o1的执行态
}
}
const o3={
text:'o3',
fn:function(){
//直接内部构造,公共人
let fn=o1.fn
return fn()//挂载在全局公共的一个方法
}
}
console.log('o1fn',o1.fn());
console.log('o2fn',o2.fn());
console.log('o3fn',o3.fn());
追问2:现在我要将concole.log('o2fn',o2,fn())的结果是o2.
(1)人为干涉,改变this--bind/apply/call
o2.fn().call(o2)
(2)不需人为改变
const o1={
text:'o1',
fn:function(){
//直接使用上下文--传统派活
console.log('o1fn',this);
return this.text
}
}
const o2={
text:'o2',
fn:o1.fn
}
console.log('o2fn',o2.fn());
(3)显式绑定(bind | apply | call)
function foo() {
console.log('函数内部', this);
}
foo()
foo.call({
a: 1
})
foo.apply({
a: 1
})
const bindFoo = foo.bind({
a: 1
})
bindFoo()
面试题:call/apply/bind的区别
1.call vs apply 传参不同 依次传入/数组传入
2.bind直接返回不同,需要再调用一次
###bind的原理/手写一个bind
//1.需求:手写bind=>bind挂载位置(挂载在哪里)=>Function.prototype
Function.prototype.newBind = function () {
//2.bind是什么?
//改变this
const _this = this
//接收参数args,第一项参数是新的this,第二项到最后一项是函数传参
const args = Array.prototype.slice.call(arguments)
console.log('args', args);
const newThis = args.shift()
console.log('newThis', newThis);
//3.返回值
return function () {
return _this.newApply(newThis, args)
}
}
Function.prototype.newApply = function (context) {
context = context || window
//挂载执行函数
context.fn=this
let result=arguments[1]
? context.fn(...arguments)
:context.fn()
delete context.fn
return result
}
###闭包:一个函数和它周围状态的引用捆绑在一起的组合
1.函数作为返回值的场景
//函数作为返回值的场景
function mail(){
let content='信'
return function(){
console.log(content);//信
}
}
const envelop=mail()
envelop()
2.函数作为参数的时候
let content=0
function envelop(fn) {
content = 1
fn()
}
function mail(){
console.log(content);//1
}
envelop(mail)//把mail函数嵌入到了envelop函数中
3.函数的嵌套
let counter = 0
function outerFn() {
function innerFn() {
counter++
console.log(counter);//1
}
return innerFn
}
outerFn()()
延伸:1.立即执行函数=>js模块化的基石
let count=0
(function immediate(args){
if(count===0){
let count=1
console.log(count);
}
})(args)
2.实现私有变量
function createStack() {
return {
items: [],
push(item) {
this.item.push(item)
}
}
}
const stack={
items:[],
push:function(){}
}
function createStack(){
const items=[]
return {
push(item){
items.push(item)
}
}
}