前端语法&规范(二)

152 阅读6分钟

这堂课讲的是前端语法规范中的8种数据类型,以及如何判断它们的类型、回收机制,以及面试最爱问的原型和原型链、闭包问题

一、JS中的8种数据类型及区别

今天我们开篇先从JS的几种数据类型说起,JS数据类型分为基本数据类型引用数据类型

  1. 基本数据类型(值类型)
    a. Number 数字
    b. String 字符串
    c. Boolean 布尔
    d. undefined 未定义
    f. symbol 符号
    g. null 空

  2. 引用数据类型(复杂数据类型)
    a. Function
    b. Array
    c. Object
    d. Date
    e. RegExp
    ......

那么我们要怎么知道一个数据它的类型是什么呢?接着往下走

二、要如何判断JS中数据类型呢

1. 可以使用 typeof 进行检测
    console.log(typeof 1);               // number
    console.log(typeof true);            // boolean
    console.log(typeof 'str');           // string
    console.log(typeof Symbol)           // function
    console.log(typeof function(){});    // function
    console.log(typeof console.log());   // function
    console.log(typeof []);              // object 
    console.log(typeof {});              // object
    console.log(typeof null);            // object
    console.log(typeof undefined);       // undefined

可以很清晰的发现用 typeof 检验数据类型的优缺点:

优点:能够区分基本数据类型

缺点:无法区分Array Object Null,都返回Object

2. 结果是返回 true 或 false 的 instanceof
    console.log(1 instanceof Number);                    // false
    console.log(true instanceof Boolean);                // false 
    console.log('str' instanceof String);                // false  
    console.log([] instanceof Array);                    // true
    console.log(function(){} instanceof Function);       // true
    console.log({} instanceof Object);                   // true

优点:能够区分Array ObjectFunction,适合用于判断自定义的类实例对象

缺点:Number Boolean String基本数据类型不能判断

3. 能精确判断类型但是写法太繁琐的 Object.prototype.toString.call()
    var toString = Object.prototype.toString;
    console.log(toString.call(1));                      //[object Number]
    console.log(toString.call(true));                   //[object Boolean]
    console.log(toString.call('mc'));                   //[object String]
    console.log(toString.call([]));                     //[object Array]
    console.log(toString.call({}));                     //[object Object]
    console.log(toString.call(function(){}));           //[object Function]
    console.log(toString.call(undefined));              //[object Undefined]
    console.log(toString.call(null));                   //[object Null]

优点:精准判断数据类型

缺点:写法繁琐不容易记,推荐进行封装后使用

综上,当碰到基本数据类型时可以使用 typeof 来判断,复杂数据类型可以使用 instanceof ,力求完美可以封装 Object.prototype.toString.call 进行复用 😁

三、JS垃圾回收机制

  1. 为什么要有垃圾回收机制呢?如果项目中存在大量不被释放的内存(堆、栈、上下文),页面性能会变得很低。当某些代码操作不能被合理释放,就会造成内存泄露。因此我们尽可能减少使用闭包,因为它会消耗内存。

  2. 浏览器垃圾回收机制 / 内存回收机制

浏览器的 JavaScript 具有自动垃圾回收机制(GC:Garbage Collection),垃圾收集器会定期(周期性)找出那些不再使用的变量,然后释放其内存。

  1. 优化手段:内存优化;手动释放:取消内存的占用
    1) 堆内存:fn = null;
    2) 栈内存: 把上下文中,被外部占用的堆取消占用即可

  2. 内存泄漏
    在js中最常见的内存泄露有4种:全局变量、闭包、DOM元素的引用、定时器

四、作用域和作用域链

作用域就是变量与函数的可访问范围,由当前环境与上层环境的一系列变量对象组成。

作用域:

  1. 全局作用域:代码在程序的任何地方都能访问到,window对象的内置属性都拥有全局作用域。

  2. 函数作用域:在固定的代码片段才能访问。

那么,作用域的作用是什么?

作用域的最大作用就是 隔离变量,如果是不同作用域下相同变量名不会有冲突。

作用域链:

一般情况下,变量到创建该变量的函数作用域中取值。但是如果在当前作用域中没有查到,就会向上级作用域去查,直到查到全局作用域,这样一个查找的过程形成的联调就叫作用域链!

五、闭包的作用:保存 / 保护

闭包的概念

函数执行时形成的私有上下文在正常情况下,代码执行完会出栈后释放;但是在特殊情况下,如果当前私有上下文中的某个东西被上下文以外的事物占用,则上下文不会出栈释放,从而形成不销毁的上下文。执行函数执行过程中,会形成一个全新的私有上下文,可能会被释放也可能不会被释放,但是不论它是不是被释放,都有保护保存的作用。
我们把函数执行形成私有上下文来 保护保存 私有变量的机制称为闭包。

闭包的用途
  1. 模仿块级作用域
  2. 保护外部函数的变量 能够访问函数定义时所在的词法作用域
  3. 封装私有化变量
  4. 创建模块
闭包的应用场景

大部分前端JavaScript代码都是“事件驱动”的,即一个事件绑定的回调方法:发送ajax请求成功、失败的回调,settimeout的延时回调,或者一个函数内部返回另一个匿名函数。这些都是闭包的应用。

闭包的优点

延长局部变量的生命周期

闭包的缺点

会导致函数的变量一直保存在内存中,过多的闭包可能导致内存泄露

六、JS中 this 的五种情况

  1. 作为普通函数执行时,this指向window
  2. 当函数作为对象的方法被调用时,this就会指向该对象
  3. 构造器调用,this指向返回的这个对象
  4. 箭头函数,箭头函数的this指向绑定的这个函数,当函数有嵌套时,指向最近一层的对象
  5. 基于function.prototype上的callapplybind调用模式,this指向的是传入的对象,call接受参数列表(用逗号分隔开参数),apply接受的参数是数组,bindcall一样但是返回的对象是函数

七、原型&&原型链

原型关系:

  • 每个class都有显示原型 prototype
  • 每个实例都有隐式原型 proto
  • 实例的_proto_指向对应class的prototype

原型

在 JS 中,当我们创造一个对象的时候,这个对象本身就会包含一些它生来就有(预定义)的属性,其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。

原型链

函数的原型链对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然类似。因此可以利用__proto__一直指向Object的原型对象上,而Object原型对象用Object.prototype.__ proto__ = null表示原型链顶端。如此形成了js的原型链继承。同时所有的js对象都有Object的基本防范

特点

JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

内容持续总结中……
参考文章:🔥 连八股文都不懂还指望在前端混下去么,总结得很全面,推荐阅读 🤩