未分类

#JavaScript基础#
JavaScript规定了2中语言类型:

原始(Primitive)类型:原始类型存储的是值

Undefined、Null、Boolean、String、Number、Symbol

对象(Object)类型:对象类型存储的是地址(指针)

Object

typeof vs instanceof 判断类型

  • typeof 对于原始类型来说,除了 null 都可以显示正确的类型
  • typeof 对于对象来说,除了函数都会显示 object
  • instanceof,内部机制是通过原型链来判断的

    instanceof 判断原始类型方法
    
    class PrimitiveString {
      static [Symbol.hasInstance](x) {
    return typeof x === 'string'
      }
    }
    console.log('hello world' instanceof PrimitiveString) // true
    

类型转换

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

闭包

闭包的定义其实很简单:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。

装箱和拆箱

装箱转换:基本类型–>对象

  • 每一种基本类型,都在对象中有对应的类,装箱机制会频繁产生临时对象。
  • 使用object函数,可以显示调用装箱能力。
  • 每一类装箱对象皆有私有的 Class 属性,这些属性可以Object.prototype.toString 获取。在 JavaScript 中,没有任何方法可以更改私有的Class 属性,因此 Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。
  • call函数本身会产生装箱操作,需要配合typeof来区分基本类型还是对象类型。

拆箱转换:对象–>基本类型

对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是原始类型了,那就不需要转换了
  • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
  • 调用 x.toString(),如果转换为基础类型,就返回转换的值
  • 如果都没有返回原始类型,就会报错

    let a = {
      valueOf() {
    return 0
      },
      toString() {
    return '1'
      },
      [Symbol.toPrimitive]() {
    return 2
      }
    }
    1 + a // => 3
    

当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。

vue框架知识

生命周期钩子函数

  • 在beforeCreate钩子函数调用的时候,是获取不到props或者data中的数据的,因为这些数据的初始化都在initState中。
  • 然后执行create的钩子函数,这个时候可以访问到之前访问不到的数据,但是这个时候组件没有被挂载,所以看不到。
  • 接下来会执行beforeMount的钩子函数,开始创建VDOM,最后执行mounted钩子,并将VDOM渲染为真实的DOM并且渲染数据。组件中如果有子组件的话,会递归挂载组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。
  • 接下来是数据更新时会调用的钩子函数beforeUpdate和updated。
  • 另外还有keep-alive独有的生命周期,分别为activated和deactivated。用keep-alive包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行deactivated钩子函数,命中缓存渲染后会执行activated钩子函数。
  • 最后就是销毁组件的钩子函数beforeDestroy和destroyed。前者适合移除事件、定时器等等,否则可能会引起内存泄露的问题。然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数。

    组件通信

    组件通信一般分为以下几种情况:

  • 父子组件通信:父组件通过 props 传递数据给子组件,子组件通过 emit 发送事件传递数据给父组件,这两种方式是最常用的父子通信实现办法

  • 兄弟组件通信:对于这种情况可以通过查找父组件中的子组件实现,也就是 this.$parent.$children,在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信。
  • 跨多层级组件通信:对于这种情况可以使用 Vue 2.2 新增的 API provide / inject

    // 父组件 A
    export default {
      provide: {
    data: 1
      }
    }
    // 子组件 B
    export default {
      inject: ['data'],
      mounted() {
    // 无论跨几层都能获得父组件的 data 属性
    console.log(this.data) // => 1
      }
    }
    
  • 任意组件:这种方式可以通过 Vuex 或者 Event Bus 解决

    extend:作用是扩展组件生成一个构造器,通常会与 $mount 一起使用。

    // 创建组件构造器
    let Component = Vue.extend({

    template: '<div>test</div>'
    

    })
    // 挂载到 #app 上
    new Component().$mount(‘#app’)
    // 除了上面的方式,还可以用来扩展已有的组件
    let SuperComponent = Vue.extend(Component)
    new SuperComponent({
    created() {
    console.log(1)
    }
    })
    new SuperComponent().$mount(‘#app’)

    mixin 和 mixins 区别

    mixin 用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的。

    Vue.mixin({

    beforeCreate() {
    // ...逻辑
    // 这种方式会影响到每个组件的 beforeCreate 钩子函数
    }
    

    })

虽然文档不建议我们在应用中直接使用 mixin,但是如果不滥用的话也是很有帮助的,比如可以全局混入封装好的 ajax 或者一些工具函数等等。

mixins 应该是我们最常使用的扩展组件的方式了。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。

另外需要注意的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并。

###computed 和 watch
一般来说需要依赖别的属性来动态获得值的时候可以使用 computed,对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch。

vm.$watch('obj', {
    // 深度遍历
    deep: true,
    // 立即触发
    immediate: true,
    // 执行的函数
    handler: function(val, oldVal) {}
})
var vm = new Vue({
  data: { a: 1 },
  computed: {
    aPlus: {
      // this.aPlus 时触发
      get: function () {
        return this.a + 1
      },
      // this.aPlus = 1 时触发
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})

###响应式原理

分享到

Fork me on GitHub