让前端飞Web前端之路前端开发

JavaScript 之 九阳神功(第一式)

2019-06-28  本文已影响5人  1kesou

招式与内功并修,根本与潮流兼顾!!!

扎实的内功

通俗易懂的讲法就是最牛逼的讲法(不认同这个观点的其实就没必要往下看了, 哈哈哈。。。)

1.变量类型与内存的关系

基本数据类型保存在栈内存中 闭包中的基本数据类型变量不保存在栈内存中,而是保存在堆内存中。

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

为了更好的搞懂变量对象与堆内存,我们结合以下例子与图解进行理解。

    // 基本数据类型-栈内存
    let a1 = 0;
    // 基本数据类型-栈内存
    let a2 = 'this is string';
    // 基本数据类型-栈内存
    let a3 = null;

    // 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
    let b = { m: 20 };
    // 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
    let c = [1, 2, 3];

image.png

因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量中获取了该对象的地址指针, 然后再从堆内存中取得我们需要的数据。

2.深拷贝和浅拷贝的区别是什么?实现一个深拷贝

浅拷贝只拷贝一层,而深拷贝是层层拷贝。

深拷贝复制变量值,对于引用类型的变量,递归至基本类型后,再复制,复制后与源对象完全隔离互不影响。

浅拷贝是将对象的每个属性进行复制,当对象的属性是引用类型时,实质复制的是其引用堆里面的指向路径,当引用指向的值发生变化时也会发生变化。

1.深拷贝最简单的实现是JSON.parse(JSON.stringify(obj))

缺点

1.对象的属性值是函数时,无法拷贝。
2.原型链上的属性无法拷贝
3.会忽略 undefined
4.不能正确的处理 Date 类型的数据
5.不能处理 RegExp
6.会忽略 symbol

2.使用 deepClone方法递归实现

  //使用递归的方式实现数组、对象的深拷贝
  function deepClone(obj, hash = new WeakMap()) {
    if (obj instanceof RegExp) return new RegExp(obj)
    if (obj instanceof Date) return new Date(obj)
    if (obj === null || typeof obj !== 'object') {
      return obj
    }
    if (hash.has(obj)) {
      return hash.get(obj)
    }
    /**
     * 如果obj 是数组, 那么obj.constructor 是 [Function: Array]
     * 如果obj 是对象, 那么obj.constructor 是 [Function: Object]
     */
    let cloneObj = new obj.constructor();
    hash.set(obj, cloneObj);
    for (let key in obj) {
      console.log(key)
      //递归
      if (obj.hasOwnProperty(key)) {
        cloneObj[key] = deepClone(obj[key], hash)
      }
    }
    return cloneObj
  }

3.如何正确判断this的指向?

谁调用它,this 就指向谁。

浏览器环境: this 都指向全局对象 window;

node 环境:this 都是空对象 {};

构造函数返回值不是 function 或 object。new Super() 返回的是 this 对象。

构造函数返回值是 function 或 object,new Super()是返回的是Super种返回的对象。

箭头函数没有自己的this,继承外层上下文绑定的this。

4.柯里化函数

概念

函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

作用

应用场景

重复调用一个函数参数很多重复的

  const curry = (fn, ...args) => args.length < fn.length
                                      ? (...arguments) => curry(fn, ...args, ...arguments) : fn(...args)
  
  function sumFn(a, b, c) {
    return a + b + c
  }
  var sum = curry(sumFn)

  console.log(sum(2, 3, 4)) // 10
  console.log(sum(2)(3)(4)) // 10
  console.log(sum(2, 3)(4)) // 10

5.let为啥变量提升了以及let const var 区别

一个变量或者方法是需要经过「创建」「初始化」「赋值」到使用的过程

created (创建)
initialized (初始化)
assigned (赋值)

                                            |      
              created x                     |            created fn
              initialized x                 |            initialized fn      
              var x = 1 <= assigned         |            assigned fn              
                                            |            funtion fn() {}
              ______________________________|_____________________________________  
                                            |  
              created x                     |             created x
              let x = 1 <= initialized      |             const x = 1 <=initialized x 
              x = 2                         |             x = 2 Error: no assign ment
                                            |  

结论补充

最后看 const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。

所谓暂时死区,就是不能在初始化之前,使用变量。

  1. let/const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
  2. 相同作用域中,let 和 const 不允许重复声明,var 允许重复声明。
  3. const 声明变量时必须设置初始值
  4. const 声明一个只读的常量,这个常量不可改变。

6.什么是闭包,什么是内存销毁

请移步 通俗易懂的解释就是最牛逼的讲法

欢迎关注公众号加群交流

干货都给你
上一篇 下一篇

猜你喜欢

热点阅读