前端基础面试题-2

1.动态绑定class类名的方法
答:js  document.getElementByClassName("xx")
    jqeury   $(".xxx")
    vue  v-bind:class

2.计算属性和watch的区别?
答:computed:通过属性计算而得来的属性
1、computed内部的函数在调用时不加()。
2、computed是依赖vm中data的属性变化而变化的,当data中的属性发生改变的时候,当前函数才会执行,
data中的属性没有改变的时候,当前函数不会执行。
3、computed中的函数必须用return返回。
4、在computed中不要对data中的属性进行赋值操作。如果对data中的属性进行赋值操作了,就是data中的属性发生改变,
从而触发computed中的函数,形成死循环了。
5、当computed中的函数所依赖的属性没有发生改变,那么调用当前函数的时候会从缓存中读取。

watch:属性监听
1、watch中的函数名称必须要和data中的属性名一致,因为watch是依赖data中的属性,当data中的属性发生改变的时候,
watch中的函数就会执行。
2、watch中的函数有两个参数,前者是newVal,后者是oldVal。
3、watch中的函数是不需要调用的。
4、watch只会监听数据的值是否发生改变,而不会去监听数据的地址是否发生改变。也就是说,watch想要监听引用类型数据的变化,
需要进行深度监听。
“obj.name”(){}------如果obj的属性太多,这种方法的效率很低,
obj:{handler(newVal){},deep:true}------用handler+deep的方式进行深度监听。
5、特殊情况下,watch无法监听到数组的变化,特殊情况就是说更改数组中的数据时,数组已经更改,但是视图没有更新。
更改数组必须要用splice()
或者set.this.arr.splice(0,1,100 )−−−−−修 改 a r r 中 第 0 项 开 始 的 1 个 数 据 为 100 ,
this.set.this.arr.splice(0,1,100)-----修改arr中第0项开始的1个数据为100,
this.set.this.arr.splice(0,1,100)−−−−−修改arr中第0项开始的1个数据为100,
this.set(this.arr,0,100)-----修改arr第0项值为100。
6、immediate:true 页面首次加载的时候做一次监听。

3.怎么理解单项数据流?
答:单向数据流就是从一个组件单方向将数据流向它的内部组件,也就是父组件的数据流向子组件中,但子组件不能将这个数据修改掉,
如要返回到父组件中修改然后重新流向子组件,从而达到更新数据的原理


4.自定义组件的语法糖  v-model是怎么实现的?
答:
主要是通过input事件来触发input标签value值来实现我们说的“双向数据绑定”,其实它还是单向数据流。上面的实际相当于
<input type="text" :value="value" @input=v=>$emit('input', v)/>
在自定义组件中使用v-model的几种方法
我们在封装输入框input、下拉选择select、单选多选radio等多会使用到自定义v-model功能。下面介绍几种常用方法的使用:

1. prop + $emit
搞过vue开发的同志们都知道我们经常用prop 和 $emit进行组件间通信,这方面不在本文具体阐述,详细请自行到cn.vuejs.org了解

<template>
    <input type="text" :value="value" @input="handleInput" :placeholder="placehodler" />
</template>
<script>
  export default {
    name: 'kInput',
    props: {
        value: ['String', 'Number'],
        placeholder: String
    },
    methods: {
        handleInput ($event) {
            // 通过input标签的原生事件input将值emit出去,以达到值得改变实现双向绑定
            this.$emit('input', $event.target.value)
        }
    }
  }
</script>
<style scoped type="less">
</style>
2. prop + $emit + model选项
<template>
    <input type="text" :value="value" @input="handleInput" />

</template>
<script>
  export default {
    name: 'kInput',
    model: {
        prop: 'value',
        event: 'input'
    },
    props: {
        value: ['String', 'Number'],
        placeholder: String
    },
    methods: {
        handleInput ($event) {
            // 通过input标签的原生事件input将值emit出去,以达到值得改变实现双向绑定
            this.$emit('input', $event.target.value)
        }
    }
  }
</script>
<style scoped type="less">
</style>
3. prop + $emit + computed
<template>
    <input type="text" :value="value2" />
    /*<app-table :data="value2"></app-table>*/
</template>
<script>
import AppTable from '@/component/common/AppTable'
  export default {
    name: 'kInput',
    props: {
        value: ['String', 'Number'],
        placeholder: String
    },
    computed: {
        value2: {
            get() {
               const v = this.value 
               return v
            },
            set(val) {
                const v = JSON.parse(JSON.stringify(this.value))//利用深拷贝原理使得修改prop值不会报错,因为prop是单向数据流,2.0版本上不允许在组件内部直接修改 
                 v = val
                 this.$emit('input', v) //这里多用于子组件间没有input元素中,通过在computed属性中监听值变化事emit input事件
            }
        }
    },
    components: { AppTable }
  }
</script>
<style scoped type="less">
</style>
三种方式则在父组件中使用
<template>
    <div class="main">
        <k-input v-model="search" placeholder="请输入搜索关键词"></k-input>
    </div>
</template>
<script>
import kInput from '@/components/common/kInput' //引入这个自定义组件(根据自己项目具体位置引入)
export default {
    data () {
      return {
        search: ''
      }
    },
    components: {
        kInput  // 局部注册组件
    }
}
</script>

5.vue-router 有哪几种钩子
答:第一种全局导航钩子

const router = new VueRouter({ ... });
router.beforeEach((to, from, next) => {
 // do someting
});

这三个参数 to 、from 、next 分别的作用:

1.to: Route,代表要进入的目标,它是一个路由对象
2.from: Route,代表当前正要离开的路由,同样也是一个路由对象
3.next: Function,这是一个必须需要调用的方法,而具体的执行效果则依赖 next 方法调用的参数
1.next():进入管道中的下一个钩子,如果全部的钩子执行完了,则导航的状态就是 confirmed(确认的)
2.next(false):这代表中断掉当前的导航,即 to 代表的路由对象不会进入,被中断,此时该表 URL 地址会 
    被重置到 from 路由对应的地址
3.next(‘/’) 和 next({path: ‘/’}):在中断掉当前导航的同时,跳转到一个不同的地址
 4.next(error):如果传入参数是一个 Error 实例,那么导航被终止的同时会将错误传递给 router.onError() 注册过的回调

第二种组件内导航钩子

组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的

const File = {
    template: `<div>This is file</div>`,
    beforeRouteEnter(to, from, next) {
        // do someting
        // 在渲染该组件的对应路由被 confirm 前调用
    },
    beforeRouteUpdate(to, from, next) {
        // do someting
        // 在当前路由改变,但是依然渲染该组件是调用
    },
    beforeRouteLeave(to, from ,next) {
        // do someting
        // 导航离开该组件的对应路由时被调用
    }
}

第三种单独路由独享组件

即单个路由独享的导航钩子,它是在路由配置上直接进行定义的:

const router = new VueRouter({
    routes: [
        {
            path: '/file',
            component: File,
            beforeEnter: (to, from ,next) => {
                // do someting
            }
        }
    ]
});

6.vue.js双向绑定原理
答:原理主要通过数据劫持和发布订阅模式实现的
通过Object.defineProperty()来劫持各个属性的setter,getter,监听数据的变化
在数据变动时发布消息给订阅者(watcher),订阅者触发响应的回调(update)更新视图。
一、什么是数据劫持
访问或者修改对象的某个属性时,都会触发相对应的函数,在这个函数里进行额外的操作或者修改返回结果。
在触发函数的时候,在函数中所做的操作,就是劫持操作。
Object.defineProperty
语法:

Object.defineProperty(obj,prop,descriptor)
参数:

obj:目标对象

prop:需要定义的属性或方法的名称

descriptor:目标属性所拥有的特性

value:属性的值
writable:如果为false,属性的值就不能被重写。
get:一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。
set:一旦目标属性被赋值,就会调回此方法。
configurable:如果为false,则任何尝试删除目标属性或修改属性性以下特性(writable, configurable, enumerable)的行为将被无效化。
enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。

7.怎么理解Vuex
答:1.vuex是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

2.vuex的五个核心概念
vue有五个核心概念,state、getters、mutations、actions、modules (plugins)。

Vuex是专门为Vue服务,用于管理页面的数据状态、提供统一数据操作的生态系统,相当于数据库mongoDB,MySQL等,任何组件都可以存取仓库中的数据。其中vuex类似的 还是有Redux,Redux大多用于React
1.小应用不建议使用Vuex,因为小项目使用 Vuex 可能会比较繁琐冗余;
2.中大型单页应用,因为要考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择;

2.1 state: 存放基本数据 ----辅助函数mapState: 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。
为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
2.2 getters: 是从store中的state派生出来的状态,专门来计算state中的数据,相当于state中数据的计算属性
—辅助函数mapGetters辅助函数: mapGetters 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性,与state类似
2.3 mutations 提交mutions是更改Vuex中的状态的唯一方法。mutations必须是同步的,如果要异步需要使用actions。
每一个mutations都有一个字符串作为第一个参数,提交载荷作为第二个参数。 —辅助函数mapMutations 将组建中的methods映射为store.commit调用。
2.4 actions 专门操作异步请求的数据和业务逻辑的地方,它不能直接变更state中的状态,而是通过commit来调用mutations里的方法来改变state里的数据。
辅助函数mapActions 将组建的methods映射为store.dispath调用
2.5 module 使用单一状态树,导致应用的所有状态几种到一个很大的对象,但是,当应用变得很大时,store对象会变得臃肿不堪,为了解决以上问题,Vuex允许我们将store分割到模块(modules)。
每个模块拥有自己的state、mutations、avtions、grtters。


8.this.$nextTick()
答:this.$nextTick 将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新。
this.$nextTick 跟全局方法 vue.nextTick 一样,不同的是,回调的 this 自动绑定到调用它的实例上。
总的来说,假设我们更改了某个 dom 元素内部的文本,而这时候我们想直接打印这个更改之后的文本是需要 dom 更新之后才会实现的,
就像我们把将要打印输出的代码放在 setTimeout(fn, 0) 中

9.slot插槽的理解
答:slot是组件内的一个占位符,该占位符可以在后期使用自己的标记语言填充。
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信方式,适用于父组件===>子组件
例子:
//父组件中
<Category>
    <div>html结构</div>
</Category>
//子组件中:
<template>
    <div>
        <slot>插槽的默认内容</slot>
    </div>
</template>

10.vue组件通信
答:方法一、props/$emit
   父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

   方法二、$emit/$on
这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,
包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
1.具体实现方式:

    var Event=new Vue();
    Event.$emit(事件名,数据);
    Event.$on(事件名,data => {});
    方法三、vuex
    方法四、$attrs/$listeners
    方法五、provide/inject
    方法六、$parent / $children与 ref
    ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
    $parent / $children:访问父 / 子实例

总结
常见使用场景可以分为三类:
父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);
ref 也可以访问组件实例;provide / inject API;$attrs/$listeners

兄弟通信:
Bus;Vuex

跨级通信:
Bus;Vuex;provide / inject API、$attrs/$listeners
11.什么是MVVM,与MVC有什么区别
答:一、什么是mvvm?
mvvm是model--view--viewmodel的简写,即模型-视图-视图模型,M(model)即数据模型,V(view)视图看到的页面,VM(view model)
视图模型相当于一个桥梁作用,连接着view和model。VM和数据进行绑定,在数据M发生变化时,将数据转化成视图。
VM也监听着V视图,当视图页面发生变化时,会更新M的数据。
二、什么是mvc?
MVC 是 Model-View- Controller 的简写。即模型-视图-控制器。M 和 V 指的意思和 MVVM 中的 M 和 V 意思一样。
C 即 Controller 指的是页面业务逻辑,使用 MVC 的目的就是将 M 和 V 的代码分离。MVC 是单向通信。也就是 View 跟 Model,
必须通过 Controller 来承上启下。
三、使用场景
场景:数据操作比较多的场景,需要大量操作 DOM 元 素时,采用 MVVM 的开发方式,会更加便捷,让开发者更多的精力放在数据的变化上,
解放繁 琐的操作 DOM 元素。
四、mvc和mvvm的区别
区别:把MVC 中 Controller 演变成 MVVM 中的 viewModel,MVVM 主要解决了 MVC 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,
影响用户体验,vue 数据驱动,通 过数据来显示视图层而不是节点操作。MVC 和 MVVM 其实区别并不大,都是一种设计思想,
 MVC 和 MVVM 的区别并不是 VM 完全取代了 C,只是在 MVC 的基础上增加了一层 VM,只不过是弱化了 C 的概念,
ViewModel 存在目的在于抽离 Controller 中展示的业务逻辑,而不是替代 Controller,其它视图 操作业务等还是应该放在 Controller 中实现,
也就是说 MVVM 实现的是业务逻辑组件的重用, 使开发更高效,结构更清晰,增加代码的复用性

12.路由跳转的方式?
答:路由跳转
方式一:path路径跳转。
传值可以使用params 传值和query传值
(缺点:不能传引用数据类型-数组,对象等)
//写法1
<router-link to="/artlist">小说列表</router-link> 
//router-link解析出来其实是a标签
//写法2
<router-link :to="path1">小说列表</router-link> 
data() {
    return{
        path1:'/artlist'
    }
}
//写法3
<router-link :to="'/artlist'">小说列表</router-link> 
data() {
    return{
        path1:'/artlist'
    }
}
方式二:命名式路由跳转(name)。
传值可以使用params和query传值
(优点:可以传基本数据类型和数组,对象)
<router-link :to="{name:'shop',query:{city:cityObj}}">购物车</router-link> 
...
//路由配置
{
    path:'/shop',
    //该path路径不能少。因为命名式路由跳转是通过name找到该path
    name:'shop',
    component:Shop
}
方式三:编程式路由跳转(最常用的,不受时机、条件的限制)。
传值可以用params 传值和query传值
(优点:可以传基本数据类型和数组,对象)

jumpHome() {
        this.$router.push({
            path:"/home",
            query:{
                id:this.id
            }
        })
      }
 ...
 //接收值如果进入另一个页面,一般在created中接收
 this.$route.query.id
 //路由配置
 { path: "/home", component: ()=>import("../Home") }

  //或者name和params搭配,接收值 this.$route.params.id
路由传值
query查询参数传值
1.1 router-link的to属性或者js方式push方法里的参数由字符串更换成对象, 需要切换的路由由path字段负责, 传递的值由query字段负责
1.2 query方式传递的值会以键值对的形式拼接到网址的后面, 与get请求传递数据的格式类似
1.3 query方式传递的值, 刷新页面, 值不会消失
1.4 query方式传值, 不需要去配置routes数组里的对应对象
params路径参数传值
2.1 router-link的to属性或者js方式push方法里的参数由字符串更换成对象, 需要切换的路由由name字段负责, 传递的值由params字段负责
2.2 params方式传递的值会以路径的形式拼接到网址的后面
2.3 params方式传递的值, 刷新页面, 值会丢失
2.4 params方式传值, 需要去配置routes数组里的对应对象, 需要给对象多加一个name字段, 还需要将path字段修改成 “/路由/:值1/:值2/:…”
//传值
methods:{
      goMyOrder(){
             this.$router.push({name:"order", params:{orderId:"667788"}}).catch(err=>{});
      }
}
//接收值
computed:{
     getOrderId(){
         return this.$route.params.orderId
     }
}
//路由配置
 { path:"order/:orderId",
   name:"order", 
   component: ()=>import("../views/Mine/MyOrder") 
 }

当进行路由重定向时, 也可以进行路由传值的, 如果需要传值, 将redirect的由字符串改成对象,例如:

        { path:" ", redirect: {path:"info", query:{userId:'112233'}} },
        { path:"info", component: ()=>import("../views/Mine/MyInfo") }

13.css选择器有哪些,并写明选择器的权重优先级
答:1、元素选择器
p,h2,span,a,div乃至html标签
2、类选择器
对文件元素添加一个class属性,点号”.”加上类名就组成了一个类选择器。
3、ID选择器
一个元素只能拥有一个唯一的ID属性。其次一个ID值在一个HTML文档中只能出现一次,
也就是一个ID只能唯一标识一个元素(不是一类元素,而是一个元素)。
4、属性选择器
 [type="text"]
5、派生选择器
(1)后代选择器
(2)子元素选择器
一个子选择器由两个或多个由">"分隔的选择器组成。
(3)相邻兄弟选择器
6.全局选择器 *
7.伪元素  : first-line ,: first-letter ,: before,: after
8.伪类  :hover,: focus
权重优先级
1. 第一等:代表内联样式,如: style="",权值为1000。
2. 第二等:代表ID选择器,如:#content,权值为0100。
3. 第三等:代表类,伪类和属性选择器,如.content,权值为0010。
4. 第四等:代表类型选择器和伪元素选择器,如div p,权值为0001。
5. 通配符、子选择器、相邻选择器等的。如*、>、+,权值为0000。
6. 继承的样式没有权值。

14.px 和 em 的区别
答:px是固定的,em会继承父级


15.position的值有哪些,有什么区别
答:position属性有4种取值static、fixed、relative、absolute,其区别是:
1、static:静态定位,是position属性的默认值,表示无论怎么设置top、bottom、right、left属性元素的位置(与外部位置)都不会发生改变。
2、relative:相对定位,表示用top、bottom、right、left属性可以设置元素相对与其相对于初始位置的相对位置。
3、absolute:绝对定位,表示用top、bottom、right、left属性可以设置元素相对于其父元素(除了设置了static的父元素以外)左上角的位置,
如果父元素设置了static,子元素会继续追溯到祖辈元素一直到body。
4、relative:生成相对定位的元素,相对于元素本身的位置进行定位,它原本所占的空间仍然会保留。
static(静态定位)是默认值,元素出现在正常的流中。不会受到top, bottom, left, right影响。定位为absolute的层脱离正常文本流,
但与relative的区别是其在正常流中的位置不再存在。

如果父级没有设定position属性,那么当前的absolute则以浏览器左上角为原始点进行定位,位置将由偏移设置(top、bottom、left、right)决定;
(这与relative完全一致)。


16.盒子垂直水平居中怎么实现?
答:方法一:利用文本水平居中text-align: center和行高line-height进行实现
方法二:利用子绝父相和外边距margin实现
先为父盒子设置相对定位,再为子盒子设置绝对定位,且绝对定位的四个方向的位移都设为0,然后将外边距margin属性值设置为auto即可。
方法三:利用子绝父相和左、上外边距实现
先为父盒子设置相对定位,再为子盒子设置绝对定位,且将子盒子分别向右、向下移动父盒子的一半,然后利用外边距margin将子盒子分别向左、
向上移动子盒子的一半。
方法四:利用子绝父相和位移实现
先为父盒子设置相对定位,再为子盒子设置绝对定位,且将子盒子分别向右、向下移动父盒子的一半,然后利用位移transform: translate;
将子盒子分别向左、向上移动子盒子的一半。
方法五:利用flex布局实现
首先在父元素中添加display:flex;使其显示模式为flex布局模式,然后在父元素中添加主轴居中和侧轴居中即可。 

17.this指向
答:(1)在全局环境中的this——window
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

         "use strict"
        console.log(this); //window
        console.log(this===window);//true
(2)在函数中的this——window
在函数内部,this的值取决于函数被调用的方式。

        function f() {
        console.log(this); //window
        console.log(this===window);//true
       }
       f()
       console.log(f()===window.f()); //true
因为定义的函数在全局作用域下定义的 

(3)函数在严格模式下——undefined
        function f() {
            "use strict"
            console.log(this); //undefined
            console.log(this === window); //false
        }
        f()
上面的f是直接调用的指向undefined 

        function f() {
            "use strict"
            console.log(this); //window
            console.log(this === window); //true
        }
        window.f()
有一些浏览器最初在支持严格模式时没有正确实现这个功能,于是它们错误地返回了window对象。

 (4)对象中的this——指向调用者
        let obj = {
            fn: function () {
                console.log(this);
            }
        }
        obj.fn() //指向obj这个对象
(5)栗子①
        function fun() {
            console.log(this.name);
        }
        let obj = {
            name: '奥特曼',
            fn: fun
        }
        var name = "怪兽"
        obj.fn()  //奥特曼
        fun()    //怪兽
obj.fn() 是obj 调用的所以去找obj里面的name 

fun是window调用的所以去找全局里面的this.name

(6)栗子②
        var obj1 = {
            name: '怪兽',
            f: function () {
                console.log('姓名:' + this.name);
            }
        }
 
        var obj2 = {
            name: '奥特曼'
        }
        obj2.f = obj1.f
        obj1.f() //姓名:怪兽
        obj2.f() //姓名:奥特曼
把obj1.赋值给obj2.f  obj2也有了f 方法 

(7)栗子③
        function foo() {
            console.log(this.a);
        }
        var obj2 = {
            a:2,
            fn:foo
        }
        var obj1={
            a:1,
            o1:obj2
        }
        obj1.o1.fn() //2
 obj1里面的o1是obj2  obj2里的fn是foo函数   在obj2里面调用的拿到obj2中的a

(8)事件绑定中的this 
 <button οnclick="Hclick()">点击事件</button>
<script>
       function Hclick() {
            console.log(this);
        }
</script>
 由于还是在当前window环境下运行的还是指向window

  <button οnclick="console.log(this)">点击事件</button>
运行在节点对象中 指向当前dom

(9)动态绑定
<button>动态绑定</button>
<script>
       let btn=  document.getElementsByTagName('button')[0].οnclick=function(){
           console.log(this);    
       }
</script>
指向当前dom

(10)addEventlistenr——当前dom
        let btn = document.getElementsByTagName('button')[0].addEventListener('click',function () { console.log(this); })
指向当前dom <button>动态绑定</button>

       let btn = document.getElementsByTagName('button')[0].addEventListener('click',()=>{
            console.log(this);
        })
换成箭头函数后 this指向当前作用域下的上级作用域的this    window 

(11)构造函数中的this——当前实例化对象
        function Pro() { 
            this.x='1'
            this.y=function(){ console.log(this);}
         }
         var p = new Pro()
         p.y()
通过构造函数创建了一个新的实例对象  所以当前的this指向新的实例对象

(12)定时器中的this——window
      setInterval(function () {console.log(this)  },1000)
this指向当前window

18.什么是原型链
答:原型链:就是实例对象和原型对象之间的链接,每一个对象都有原型,原型本身又是对象,原型又有原型,以此类推形成一个链式结构.称为原型链

 19.let var const的区别
答:var、let和const的区别
let和const是ES6新增的关键字,如果还不知道ES6的小伙伴们,建议好好去了解下。

区别1

let和var用来声明变量,const用来声明常量。

变量就是赋值后可以改变它的值,常量就是赋值后就不能改变它的值。

区别2

const不允许只声明不赋值,一旦声明就必须赋值

错误的写法

const num;
正确的写法

const num = 4;
区别3

var是函数作用域,let和const是块级作用域。

花括号{}就是块级作用域,函数作用域就是函数里面的内容。

对比这两段代码

{
    let num = 4;
}
console.log(num);// num is not defined
{
    var num = 4;
}
console.log(num); // 4
区别4

var有提升的功能,let和const没有

console.log(a);  //undefined
var a = 4;
console.log(a); //a is not defined
let a = 4;
区别5
在最外层的作用域,也就是全局作用域,用var声明的变量,会作为window的一个属性
var a = 4;
function foo(){
    var b = 5;
    console.log("b=>"+b) // 5
    console.log("window.b=>"+window.b) // undefined
    console.log("window.a=>"+window.a) // 4
}
foo()
console.log("a=>"+a) // 4
console.log("window.a=>"+window.a) // 4
console.log("window.b=>"+window.b) // undefined
而用let和const声明的变量或常量,并不会作为window的属性

对比下面两段段代码
var a = 4;
function foo(){
    /*
    这里的this采用默认的规则,与window进行了绑定,所以实际上访问的是window.a
    */
    console.log(this.a);// 4  
}
foo()
let a = 4;
function foo(){
    /*
    在这种情况下,this.a 访问的是window.a,但是let定义的变量,并不会作为window的属性,所以访问不到
    */
    console.log(this.a);// undefined  
}
foo()


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值