java上下文域_上下文和作用域

新的声明方式,新的声明方式带来了什么?块级作用域

在let之前,js只有全局作用域和函数作用域,先上个栗子吧

if(false){

var aa = 111;

}

console.log(aa) // 111

for(var i=0;i<5;i++){

var bb = 222;

}

console.log(i) //5

console.log(bb) //222

其实从if这个单词开始到大括号结束,从for这个单词开始到大括号结束是有作用域的,叫做块级作用域块级作用域用逻辑判断跳出,return不能跳出块级作用域,只能跳出函数作用域,他能被外部访问是因为var的变量提升和函数提升,除掉一个习惯,函数的声明别放在if和for里,那就只剩变量提升了,如果把上面的代码改为let,const跟let一样的,不讲是因为没人会把const当变量

if(false){

let aa = 111;

}

console.log(aa) // 错误

for(let i=0;i<5;i++){

let bb = 222;

}

console.log(i) //错误

console.log(bb) //错误

let让上面的aa,i,bb只能在那个小小的作用域内使用,如果同个函数作用域或者全局作用域有一样的变量,会安全很多,如果觉得没什么意义的话,就接着用var,没区别

let/const跟var有什么区别

// 面试题来着

// 这里是全局作用域window

var aa = "aa"

let bb = "bb"

console.log(window.aa) //"aa"

console.log(window.bb) //undefined

console.log(window.cc) //undefined

console.log(aa) //"aa"

console.log(bb) //"bb"

// bb并不在window里,因为他在自己的作用域里,只能用变量名去取值

所以目前已知作用域为全局作用域,函数作用域,块级作用域,用代码模拟就是

// 当页面被打开,就有一个全局作用域window

window = {

// 还有页面里所有的dom和bom

var dom = {}

var bom = {}

// 下面的内容就是程序员自己的代码或者插件了

var name = "tom"

function aa(){

// 函数作用域

if(true){

// 块级作用域

let name = "jee"

}

console.log(name) //"tom"

}

}

上下文

回顾一个函数作用域里隐藏了什么东西

有个隐藏的arguments参数伪数组,如果是dom时间,还有个event事件对象,并且还有个肯定有的this上下文,函数被执行肯定有个执行者,执行者就是这个函数的this

// 这个都理解不了就转行吧

var name = "我是window"

function init(){

console.log(this) // window

console.log(this.name) // "我是window"

}

init() //实际是window.init()

// 这个都理解不了就转行吧

var obj = {

name: "我是obj",

init(){

console.log(this) // obj

console.log(this.name) // "我是obj"

}

}

obj.init()

如果我不想把init写在obj里,因为window我也要用,重复写很不好怎么办

用function的方法apply,call,bind

这三兄弟是面试的经典

var name = "我是window"

var obj = {

name: "我是obj",

}

// 加两个

function init(x,y){

console.log(x,y)

console.log(this)

console.log(this.name)

}

// 直接执行init肯定就是window了

init(1,2)

// 修改this方式一,立即执行

init.call(obj,1,2)

// 修改this方式二,立即执行

init.apply(obj,[1,2])

// 修改this方式三,不立即执行,后续执行

init.bind(obj)(1,2)

说apply

因为apply可以把参数变成数组,所以这个强大的优点让人们忘了他实际是用来改变this的

Math.max.apply(null, [14, 3, 77]) //第一个参数是空因为Math本来就是window的子对象

arr1.push.apply(arr1,arr2) //第一个参数需要是this自己,用null就没了

// 还可以这么写

Array.prototype.push.apply(arr1, arr2);

...

//一切可以无限传参数的方法都可以用apply改成传数组

但是apply被拓展运算符代替了

Math.max(...[14, 3, 77])

arr1.push(...arr2);

arr1.push(...arguments);

保留this

在函数里执行函数肯定是window

function init(){

console.log(this)

}

document.querySelector("#id").onclick = function(){

console.log(this) //dom#id....

console.log(this.style.color)

init() // 我们总是以为这个是当前this执行的,但实际是window执行的

}

为了让init能拿到前一个函数的this,最常见的做法是var that = this

然后把that当做参数传过去,这个是没问题的,只是维护很难,很吐血,判断很多

function cb(that){

console.log(that)

}

var obj = {

name: "我是obj",

init(cb){

console.log(this)

var that = this;

cb(that)

}

}

obj.init(cb)

上面的call,apply,bind直接修改上下文this,而不是把上下文当做参数去传递

function cb(that){

console.log(that)

}

var obj = {

name: "我是obj",

init(cb){

console.log(this)

cb.call(this)

}

}

obj.init(cb)

箭头函数也可以保存this,但是必须声明在父函数内部

var obj = {

name: "我是obj",

init(){

console.log(this)

// 箭头函数执行是往上找执行者,直到找到一个执行者后停下来

// 如果如果上一级也是箭头函数就继续往上找

// 箭头函数没有三个改变this的方法

// 一般也不会在这里声明一个cb函数,cb函数一般都是作为参数传进来的

var cb = () => { console.log(this) };

cb() // obj

}

}

obj.init()

箭头函数在定时器效果最明显

var name = "name1";

function init(){

var name = "name2";

// 这个改的是window的name

setTimeout(function(){

this.name = "new"

},2000)

// 这个改的是上面的name

setTimeout(()=>{

this.name = "new"

},2000)

}

this的使用是很少的,也不会需要经常去修改this,自定义插件就用的挺多的

上班的时候学习java后自己做了个js的面向AOP开发的功能

function aop(funArr,beforeFun,afterFun,context){

var thatContext = context;

var that = this;

this.init = function(){

that.funArrEach(funArr,thatContext);

return that;

}

this.funArrEach = function(arr,context){

arr.forEach(function(fun){

that.addAop(fun,context)

})

}

this.addAop = function(fun,context){

var context = context || window;

context[fun.name] = function(){

beforeFun()

fun.call(context,...arguments)

afterFun()

}

}

this.push = function(opt,context){

var context = context || thatContext;

if(Object.prototype.toString.call(opt)=="[object Array]"){

that.funArrEach(opt,context);

}

if(Object.prototype.toString.call(opt)=="[object Function]"){

that.addAop(opt,context);

}

}

}

function aopBeforeFun() { ... }

function aopAfterFun() { ... }

new aop([a,b,c],aopBeforeFun,aopAfterFun).init()

改进版,有时候aopBefore需要判断是否执行

this.addAop = function(fun,context){

var context = context || window;

context[fun.name] = function(){

if(beforeFun(fun,context,arguments)){

}else{

fun.call(context,...arguments)

}

afterFun()

}

}

function aopBeforeFun(cb,context,arg) {

if(true){

// 自己执行

cb.call(context,...arg)

return true;

}else{

// 跳过不执行了

return true

}

}

aopAfter也可以按上面这么改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值