Contents
  1. 1. 1. let,const 和 var
    1. 1.0.0.0.1. var
    2. 1.0.0.0.2. let
    3. 1.0.0.0.3. 区别:
    4. 1.0.0.0.4. const
    5. 1.0.0.0.5. 顶层对象的属性
  • 2. 2. mvc 和 mvvm
    1. 2.0.1. mvc = Model View Controller
    2. 2.0.2. mvvm = model - viewmodel -view
    3. 2.0.3. 数据双向绑定
      1. 2.0.3.0.1. 实现双向绑定的做法
  • 2.0.4. vue是什么?
  • 3. 3. js作用域和闭包
    1. 3.0.1. 作用域
    2. 3.0.2. 闭包
  • 4. 4. v-model
  • 5. 5. toast
  • 6. 6. session,cookies
    1. 6.0.1. cookies
    2. 6.0.2. Web Storage
    3. 6.0.3. cookie 和 sessionStorage localStorage
  • 7. 7. 中间件
  • 1. let,const 和 var

    var

    如果使用关键字 var 声明一个变量,那么这个变量就属于当前函数作用域
    如果声明是发生在任何函数的顶层声明,那么这个变量就属于全局作用域。

    let

    ES6新增,用于声明变量,用法和 var 类似,但是所声明的变量只在let 命令所在的代码块内有效

    举个例子:

    • 对于var:
      1
      2
      3
      4
      5
      6
      7
      var a=[]
      for(var i=0;i<10;i++){
      a[i] = function(){
      console.log(i)
      }
      }
      a[6](); //10

    在这个代码中,变量i 是var声明的,在全局范围内都有效,所以全局只有一个变量 i,每一次循环,变量i 的值都会发生改变,而循环内,被赋给数组a 的函数内部console.log(i) 中的i 指向全局的i 。也就是说,所有数组a的成员中的i 所指向的都是同一个i ,导致运行时最后输出的是最后一轮的i 值

    • 对于 let
      1
      2
      3
      4
      5
      6
      7
      var a=[]
      for(let i=0;i<10;i++){
      a[i] = function(){
      console.log(i)
      }
      }
      a[6](); //10

    在这个代码中,变量 i 是let声明的,当前的i 只在本轮循环内有效,所以每一次循环的i 其实都是一个新的变量
    问题1
    如果每一轮循环的变量 i都是重新声明的,那它怎么知道上一轮循环的值从而计算出本轮循环的值?

    这是因为js 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算

    问题2
    for 循环有一个特别之处,就是设置循环变量的那部分其实是一个父作用域,而循环体内部是一个单独的子作用域。标明函数内部的变量 i和循环变量i不在同一个作用域,而是各有各自单独的作用域。

    区别:
    1. 不存在变量提升
      1
      2
      3
      4
      5
      6
      7
      // var
      console.log(foo)//undefined
      var foo = 2

      // let
      console.log(bar)
      let bar = 2; //ReferenceError

    let 改变了语法行为,它所声明的变量一定要在声明后使用,否则就会报错

    1. 暂时性死区

    本质
    只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现时,才可以获取和使用该变量


    在这里我涉及一点也是面试官问到我的问题:有关作用域,

    • 对于var而言,只要进入该作用域,所要使用的变量就已经存在,会被赋值为undefined,可以使用和和获取
    • 而对于let而言,只要进入该作用域,所要使用的变量也会存在,但是不能使用和获取
      (我想,这应该是面试官想听见的)

    只要块级作用域里内存在let命令,它所声明的变量就绑定这个区域,不再受外部的影响

    1
    2
    3
    4
    5
    var tmp = 123
    if(true){
    tmp = 123 //ReferenceError
    let tmp
    }

    注意
    ES6规定,如果区块中存在let 和const 这两个命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。

    1. 不允许重复声明
      let 不允许在相同作用域内重复声明一个变量
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      //Error
      function(){
      var a = 15;
      let a = 2;
      }
      //Error
      function() {
      let a = 15;
      let a = 8;
      }
    const
    • 声明一个只读的常量,一旦声明,常量的值就不可以改变。
    • const一旦声明常量,就必须立即初始化,不能留到以后赋值
    • const 命令声明的常量作用域与let相同,只在声明所在的块级作用域有效,不会变量提升,同样存在暂时性死区,只能在声明后使用

    本质
    实际上保证的并不是变量的值不得改动,二是变量指向的那个内存地址不得改动;
    const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,这不能控制


    顶层对象的属性

    顶层对象

    • 在浏览器中指的是windows对象
    • 在node中指的是global对象
    • ES5中,顶层对象与全局变量是等价的

    • var命令和 function 命令生成的全局变量是顶层对象的属性
    • let,const,class 命令声明的全局变量不属于顶层对象的属性
    let const var
    变量 常量 常量
    不存在变量提升 不存在变量提升 存在变量提升(undefined)
    暂时性死区 暂时性死区 不存在暂时性死区
    不允许重复赋值 不允许重复赋值 允许重复赋值
    声明后允许不赋值(undefined) 声明后必须初始化 声明后允许不赋值(undefined)

    2. mvc 和 mvvm

    mvc = Model View Controller

    模型 - 视图 - 控制器

    1. model 和 view 永远不能相互通信,只能通过 controller 传递
    2. controller 可以直接与 model 对话(读写调用model),model 通过notification 和 kvo 机制与controller 间接通信
    3. Controller可以直接与View对话,通过outlet,直接操作View,outlet直接对应到View中的控件,view 通过action向controller报告事件的发生(比如用户touch我了)。controller是view的直接数据源(数据很可能是controller 从model 中取得并经过加工了)。controller 是view 的代理。

    缺点
    在通常的开发中,除了简单的model,view 以外的所有部分都被放在了controller中,controller 负责显示界面,响应用户的操作,网络请求以及与model交互,这就导致了controller

    1. 逻辑复杂,难以维护
    2. 和view 紧耦合,无法测试

    mvvm = model - viewmodel -view

    一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构

    • model: 数据的拥有者,实现具体的业务逻辑
    • ViewModel: 属于view+model,是一个放置用户输入验证逻辑,视图显示验证,发起网络等其他的地方。换句话说,就是把原来的View Controller 层的业务逻辑和页面逻辑等剥离出来放到viewModel层
    • view层:就是view层和viewController层,他的任务就是从view层获得数据,然后显示
      mvvm

    mvvm的优点

    1. mvvm降低了一个视图控制器的复杂性
    2. mvvm与现有的mvc架构兼容
    3. mvvm使程序更容易测试
    4. mvvm适合使用绑定机制

    mvvm 采用数据双向绑定,v的变动直接反应在vm上,m的变化也直接反应在vm上

    数据双向绑定

    文章详情:https://github.com/DMQ/mvvm

    实现双向绑定的做法
    1. 发布者- 订阅模式(backbone.js)
      一般通过sub,pub的方式实现数据和视图的绑定监视,更新数据的方式通常是vm.set('property',value)
      我们更希望的是通过vm.set('property',value)这种方式更新数据,同时更新视图、

    2. 脏值检查(angular.js)
      angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,angular只有在指定的事件触发时进入脏值检测(最简单的方式就是通过setInterval()定时轮询检测数据变动)

    3. 数据劫持(vue.js)
      vue.js是通过采用 数据劫持 的方式结合 发布者-订阅者 的方式,通过object.definePrpperty()来劫持各个属性的settergetter在数据变动时发布消息给订阅者,触发相应的监听回调

    vue是什么?

    vue是一个构建用户界面的框架库,目标是通过尽可能简单的api 实现相应的数据绑定和组合的视图集合,vue自身不是一个全能的框架,核心只是关心视图层。

    3. js作用域和闭包

    作用域

    记得之前写过一篇作用域的文章
    https://blog.csdn.net/Welkin_qing/article/details/80673682

    先来介绍三个职位:

    • 引擎: 从头到尾,负责整个js 程序的编译及执行过程
    • 编译器: 引擎的好朋友之一,负责语法分析及代码生成等脏活和累活
    • 作用域: 引擎的另一位好朋友,负责收集并维护由所有变量(声明的标识符)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些变量的访问权限

    作用域分为两个步骤:1. 声明 2. 查找
    首先编译器会在当前作用域声明一个变量(如果之前没声明过),其次,在运行时引擎会在作用域查找该变量,如果找到会对它赋值。

    闭包

    • 简单的理解是函数的嵌套形成闭包,闭包包括函数本身以及它的外部作用域
      闭包是指有权访问另一个函数作用域中的变量的函数
    • 创建闭包的常见方式就是:在一个函数内部创建另一个函数
    • 使用闭包可以形成独立的空间,延长变量的生命周期,包括中间状态值

    4. v-model

    引述:https://blog.csdn.net/xidongdong1/article/details/79539243

    v-model虽然很像使用了双向数据绑定的 Angular 的 ng-model,但是 Vue 是单项数据流,v-model 只是语法糖而已。

    第一行的代码其实只是第二行的语法糖。

    1
    2
    <input v-model="sth" />
    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />

    然后第二行代码还能简写成这样:

    1
    <input :value="sth" @input="sth = $event.target.value" />

    要理解这行代码,首先你要知道 input 元素本身有个 oninput 事件,这是 HTML5 新增加的,类似 onchange ,每当输入框内容发生变化时,就会触发oninput,把最新的value传递给 sth。

    我们仔细观察语法糖和原始语法那两行代码,可以得出一个结论:

    在给元素添加v-model属性时,默认会把value作为元素的属性,然后把’input’事件作为实时传递value的触发事件

    5. toast

    引述:https://www.jianshu.com/p/ae0c4a055cce
    步骤:

    1. 初始化toast:
      使用section设计toast 弹出内容,确保一个页面只有一个toast,对这个toast 设置隐藏使用section设计toast 弹出内容,确保一个页面只有一个toast,对这个toast 设置隐藏
    2. 显示toast
      • 显示toast时,需要确保上一个TimeOut已经被清空
      • 判断弹出内容,不能为空
    3. 隐藏toast
      判断TimeOut 是否存在,若存在则清空
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    /**
    * 用原生 JS 封装一个 Toast 组件
    */
    var Toast = {
    // 隐藏的 setTimeOut 引用
    hideTimeOut: null,
    /**
    * 初始化
    */
    init: function () {
    var toastNode = document.createElement('section');
    toastNode.innerHTML = '<i class="iconfont icon-success"></i><i class="iconfont icon-error"></i><span class="text">111</span>';
    toastNode.id = 'toastWaka'; // 设置id,一个页面有且仅有一个Toast
    toastNode.setAttribute('class', 'toast'); // 设置类名
    toastNode.style.display = 'none'; // 设置隐藏
    document.body.appendChild(toastNode);
    },
    /**
    * 显示Toast
    * @param text 文本内容
    * @param type 类型 success error
    * @param duration 持续时间
    */
    show: function (text, type, duration) {
    // 确保上一次的 TimeOut 已被清空
    if (this.hideTimeOut) {
    clearTimeout(this.hideTimeOut);
    this.hideTimeOut = null;
    // console.error('上一次的 TimeOut 还未走完!');
    // return;
    }
    if (!text) {
    console.error('text 不能为空!');
    return;
    }
    var domToastWaka = document.getElementById('toastWaka');
    console.log('domToastWaka', domToastWaka);
    if (!domToastWaka) {
    console.error('toastWaka DOM 不存在!');
    return;
    }
    var domIconSuccess = domToastWaka.querySelector('.icon-success'); // 成功图标
    var domIconError = domToastWaka.querySelector('.icon-error'); // 错误图标
    var domToastText = domToastWaka.querySelector('.text'); // 文字
    domToastText.innerHTML = text || '';
    switch (type) {
    case 'success':
    domIconSuccess.style.display = 'inline';
    domIconError.style.display = 'none';
    break;
    case 'error':
    domIconSuccess.style.display = 'none';
    domIconError.style.display = 'inline';
    break;
    default:
    domIconSuccess.style.display = 'none';
    domIconError.style.display = 'none';
    break;
    }
    domToastWaka.style.display = 'block';
    // 不传的话默认2s
    var that = this;
    this.hideTimeOut = setTimeout(function () {
    domToastWaka.style.display = 'none';
    that.hideTimeOut = null; // 置 TimeOut 引用为空
    }, duration || 2000);
    },
    /**
    * 隐藏 Toast
    */
    hide: function () {
    // 如果 TimeOut 存在
    if (this.hideTimeOut) {
    // 清空 TimeOut 引用
    clearTimeout(this.hideTimeOut);
    this.hideTimeOut = null;
    }
    var domToastWaka = document.getElementById('toastWaka');
    if (domToastWaka) {
    domToastWaka.style.display = 'none';
    }
    }
    };

    Toast.init();
    Toast.show('123', 'success', 10000);
    setTimeout(function () {
    Toast.hide();
    }, 3000);

    6. session,cookies

    cookies

    cookie的作用是与服务器进行交互(在浏览器和服务器来回传递),作为HTTP规范的一部分而存在。
    服务器和客户端都可以访问,大小只有4kb左右;存在有效期,过期后将会被删除。
    Cookies 的大小是受限的,并且每次你请求一个新的页面的时候Cookies都会被发送过去,这样无形浪费了带宽,另外,cookie还需要指定作用域,不可以跨域调用。

    Web Storage

    web storage 仅仅是为了在本地“存储”数据而生。
    除此之外,web Storage 拥有setItem,getItem,removeItem,clear 等方法,不像cookie 需要前端开发者自己封装setCookie,getCookie。

    • session:(sessionStorage)中的数据,这些数据只有在同一个会话找那个的页面才能访问,并且当会话结束后数据也随之销毁,因此session 不是一种持久化的本地存储,仅仅是会话级别的存储。

    • localStorage:(本地存储)用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期。只有本地浏览器端可以访问数据,服务器不能访问本地存储直到故意通过POST 或者GET的通道发送到服务器;每个域5MB,没有过期数据,它将保留直到用户从浏览器清除或者使用js 代码清除。

    • sessionStorage 和 localStorage 的存储空间更大
    • sessionStorage 和 localStorage 有更多的丰富易用的接口
    • sessionStorage 和 localStorage各自独立的存储空间

    7. 中间件

    引文:http://expressjs.com/en/guide/using-middleware.html
    我之前写过一篇博客:https://blog.csdn.net/Welkin_qing/article/details/84175499

    提及一下express:
    express 是一个路由和中间件Web框架,它具有自己的最小功能:Express应用程序本质上是一系列中间件函数调用。

    Express 应用程序可以使用以下类型的中间件:

    • 应用程序级中间件
    • 路由器级中间件
    • 错误处理中间件
    • 内置中间件
    • 第三方中间件

    可以使用可选的装载路径加载应用程序级和路由器级中间级。还可以将一系列中间件功能加载在一起,从而在安装点创建中间件系统的子堆栈。
    (详情见引述)


    中间件函数是可以访问请求对象(req),响应对象(res)以及应用程序的请求,去响应周期中的下一个中间件函数的函数,下一个中间件函数通常由名为 next 的变量表示。

    中间件的功能可以执行以下任务:

    • 执行任何代码
    • 更改请求和响应对象
    • 结束请求 - 响应循环
    • 调用堆栈中的下一个中间件函数

    如果当前的中间件函数没有结束请求 - 响应周期,则必须调用 next() 以将控制传递给下一个中间件函数。否则,请求将被挂起。

    Contents
    1. 1. 1. let,const 和 var
      1. 1.0.0.0.1. var
      2. 1.0.0.0.2. let
      3. 1.0.0.0.3. 区别:
      4. 1.0.0.0.4. const
      5. 1.0.0.0.5. 顶层对象的属性
  • 2. 2. mvc 和 mvvm
    1. 2.0.1. mvc = Model View Controller
    2. 2.0.2. mvvm = model - viewmodel -view
    3. 2.0.3. 数据双向绑定
      1. 2.0.3.0.1. 实现双向绑定的做法
  • 2.0.4. vue是什么?
  • 3. 3. js作用域和闭包
    1. 3.0.1. 作用域
    2. 3.0.2. 闭包
  • 4. 4. v-model
  • 5. 5. toast
  • 6. 6. session,cookies
    1. 6.0.1. cookies
    2. 6.0.2. Web Storage
    3. 6.0.3. cookie 和 sessionStorage localStorage
  • 7. 7. 中间件