vue源码解析 —— 知识准备

版权声明:原创文章,未经允许不得转载!!! https://blog.csdn.net/halo1416/article/details/84575754

学习vue源码之前,先学习一些要用到的知识,即一下6点:

1. [].slice.call(lis): 将伪数组转换为真数组

2. node.nodeType: 得到节点类型

3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)

4. Object.keys(obj): 得到对象自身可枚举属性组成的数组

5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性

6. DocumentFragment: 文档碎片(高效批量更新多个节点)

解析:

举例操作的DOM元素结构如下:

1. [].slice.call(lis): 根据伪数组生成对应的真数组

        伪数组:是一个特别的对象, length和数值下标等数组的属性

        .call():让一个函数在指定对象上调用

        另一种写法:Array.prototype.slice.call() :效果一致,效率更高(因为少创建了一次数组,少占用内存,并且数组还需要带原型链上找 slice() 方法)

2. node.nodeType: 得到节点类型

        节点大小:Docment > Element > Attribute > Text (即文档 > 元素 > 属性 > 文本)

        nodeType的值:Element => 1;Attribute => 2;Text => 3

3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)

        3.1 数据描述符

                configurable:是否可以重新define(定义,默认false),为false再次定义将报错

                enumerable:是否可以枚举(默认 true),即使用, for..in或Object.key()是否可以读取到定义的属性

                value:初始值(默认undefined)

                writable:是否可以修改初始值(默认false)

        3.2 访问描述符

                get:回调函数,根据其它相关的属性动态的计算得到当前的属性值

                set:回调函数,监视当前属性值的变化,更新其它相关的属性值

        注意:get 和 set 相当于vue 中watch的get、set,其实现的原理就是来自于这里;

                   vue中的数据绑定、数据代理均来自于 Object.defineProperty 这个方法;但这个方法并不支持IE8(这个方法来自                              于 ES5新增),所有最终导致了vue项目不支持IE8;其原因就是因为vue的核心语法中用的了这个方法!
示例1:

示例2:

4. Object.keys(obj): 得到对象自身可枚举属性组成的数组

5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性  ==> obj是Object的一个实例,即一个实际的对象

6. DocumentFragment: 文档碎片(高效批量更新多个节点)

      document: 对应显示的页面, 包含n个elment 一旦更新document内部的某个元素界面更新  (如有10个li,界面会遍历刷新10次)

      documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变

      即从多次刷新变成了一次刷新

最后,完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="test">尚硅谷IT教育</div>

<ul id="fragment_test">
  <li>test1</li>
  <li>test2</li>
  <li>test3</li>
</ul>


<!--
1. [].slice.call(lis): 将伪数组转换为真数组
2. node.nodeType: 得到节点类型
3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
6. DocumentFragment: 文档碎片(高效批量更新多个节点)
-->

<script type="text/javascript">
  //1. [].slice.call(lis): 根据伪数组生成对应的真数组
  const lis = document.getElementsByTagName('li') // lis是伪数组(是一个特别的对象, length和数值下标属性)
  console.log(lis instanceof Object, lis instanceof Array)
  // 数组的slice()截取数组中指定部分的元素, 生成一个新的数组  [1, 3, 5, 7, 9], slice(0, 3)
  // slice2()
  Array.prototype.slice2 = function (start, end) {
    start = start || 0
    end = start || this.length
    const arr = []
    for (var i = start; i < end; i++) {
      arr.push(this[i])
    }
    return arr
  }
  const lis2 = Array.prototype.slice.call(lis)  // lis.slice()
  console.log(lis2 instanceof Object, lis2 instanceof Array)
  // lis2.forEach()

  //2. node.nodeType: 得到节点类型
  const elementNode = document.getElementById('test')
  const attrNode = elementNode.getAttributeNode('id')
  const textNode = elementNode.firstChild
  console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType)

  //3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
  const obj = {
    firstName: 'A',
    lastName: 'B'
  }
  //obj.fullName = 'A-B'
  Object.defineProperty(obj, 'fullName', {
    // 属性描述符:

    // 数据描述符

    //访问描述符
    // 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
    get () {
      return this.firstName + "-" + this.lastName
    },
    // 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
    set (value) {
      const names = value.split('-')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  })

  console.log(obj.fullName) // A-B
  obj.fullName = 'C-D'
  console.log(obj.firstName, obj.lastName) // C D

  Object.defineProperty(obj, 'fullName2', {
    configurable: false, //是否可以重新define
    enumerable: true, // 是否可以枚举(for..in / keys())
    value: 'A-B', // 指定初始值
    writable: false // value是否可以修改
  })
  console.log(obj.fullName2)  // A-B
  obj.fullName2 = 'E-F'
  console.log(obj.fullName2) // A-B

  /*Object.defineProperty(obj, 'fullName2', {
    configurable: true,
    enumerable: true,
    value: 'G-H',
    writable: true
  })*/


  //4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
  const names = Object.keys(obj)
  console.log(names)


  //5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
  console.log(obj.hasOwnProperty('fullName'), obj.hasOwnProperty('toString'))  // true false

  //6. DocumentFragment: 文档碎片(高效批量更新多个节点)
  // document: 对应显示的页面, 包含n个elment  一旦更新document内部的某个元素界面更新
  // documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变
  /*
  <ul id="fragment_test">
    <li>test1</li>
    <li>test2</li>
    <li>test3</li>
  </ul>
   */
  const ul = document.getElementById('fragment_test')
  // 1. 创建fragment
  const fragment = document.createDocumentFragment()
  // 2. 取出ul中所有子节点取出保存到fragment
  let child
  while(child=ul.firstChild) { // 一个节点只能有一个父亲
    fragment.appendChild(child)  // 先将child从ul中移除, 添加为fragment子节点
  }

  // 3. 更新fragment中所有li的文本
  Array.prototype.slice.call(fragment.childNodes).forEach(node => {
    if (node.nodeType===1) { // 元素节点 <li>
      node.textContent = 'atguigu'
    }
  })

  // 4. 将fragment插入ul
  ul.appendChild(fragment)

</script>
</body>
</html>

文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!

猜你喜欢

转载自blog.csdn.net/halo1416/article/details/84575754