一名【合格】前端工程师的自检清单(变量和类型篇)

    xiaoxiao2022-07-02  104

    #一、JavaScript基础

     

    ##变量和类型

     

    ###1.JavaScript规定了几种语言类型

     

    JavaScript规定了七种语言类型,他们是:

    Undefined、Null、Boolean、String、Number、Symbol、Object

     

    ###2.JavaScript对象的底层数据结构是什么

    表意不明

     

    ###3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol

     

    Symbol 值通过Symbol函数生成。这就是说,对象的属性名可以有两种类型:一种是字符串,另一种是Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

     

    应用:

     

    let firstName = Symbol("first name");

    let person = {};

    person[firstName] = "huochai";

    console.log("first name" in person); // false

    console.log(person[firstName]); // "huochai"

    console.log(firstName); // "Symbol(first name)"

     

    Symbol的描述被存储在内部[[Description]]属性中,只有当调用Symbol的toString()方法时才可以读取这个属性。在执行console.log()时隐式调用了firstName的toString()方法,所以它的描述会被打印到日志中,但不能直接在代码里访问[[Description]]

     

    实现:

     

    暂无思路

     

    ###4.JavaScript中的变量在内存中的具体存储形式

     

    大家都知道,JavaScript中的变量类型分为两种,一种是基本数据类型,包括:undefined,null,Number,String,Boolean,另外一种就是对象。两种数据类型的存储方式在JS中也有所不同。

     

    内存分为栈区(stack)和堆区(heap)

     

    基本数据类型:基本数据类型值指保存在栈内存中的简单数据段。访问方式是按值访问。

    例如var a = 1;

     

    引用数据类型:引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。

    按引用访问,读写它们时需要先从栈中读取堆内存地址,然后找到保存在堆内存中的值。

    例如var a = new Object();

     

    基本数据类型保存在栈内存,引用数据类型实际上是一个指针,这个指针也保存在栈中,但是这个指针指向的对象则保存在堆内存中。

     

    ###5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作

    表意不明

     

    ###6.理解值类型和引用类型

     

    (1)值类型(基本类型):字符串(string)、数值(number)、布尔值(boolean)、undefined、null (这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值)(ECMAScript 2016新增了一种基本数据类型:symbol http://es6.ruanyifeng.com/#docs/symbol )

     

    (2)引用类型:对象(Object)、数组(Array)、函数(Function)

     

    (1)值类型:

    1、占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。)

     

    2、保存与复制的是值本身

     

    3、使用typeof检测数据的类型

     

    4、基本类型数据是值类型

     

    (2)引用类型:

    1、占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)

     

    2、保存与复制的是指向对象的一个指针

     

    3、使用instanceof检测数据类型

     

    4、使用new()方法构造出的对象是引用型

     

    ###7.null和undefined的区别

     

    null: Null类型,代表“空值”,代表一个空对象指针,使用typeof运算得到 “object”,所以你可以认为它是一个特殊的对象值。

     

    undefined: Undefined类型,当一个声明了一个变量未初始化时,得到的就是undefined。

     

    null是javascript的关键字,可以认为是对象类型,它是一个空对象指针,和其它语言一样都是代表“空值”,不过 undefined 却是javascript才有的。undefined是在ECMAScript第三版引入的,为了区分空指针对象和未初始化的变量,它是一个预定义的全局变量。没有返回值的函数返回为undefined,没有实参的形参也是undefined。

     

    ###8.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

     

    ####判断JavaScript数据类型的方式:

     

    1、typeof:(可以对基本类型(包括function)做出准确的判断,但对于引用类型,用它就有点力不从心了)

     

    typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function、Symbol6种数据类型。

     

    对于引用类型,返回的都是object,其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的`祖宗`。 但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了。

     

    注意:typeof null也是返回object

     

    2、instanceof

     

    判断对象和构造函数在原型链上是否有关系,如果有关系,返回真,否则返回假

     

    function Aaa(){

    }

    var a1 = new Aaa();

    console.log( a1 instanceof Aaa); //true 判断a1和Aaa是否在同一个原型链上,是的话返回真,否则返回假

    var arr = [];

    console.log( arr instanceof Aaa); //false

     

    3、constructor:查看对象对应的构造函数

     

    constructor 在其对应对象的原型下面,是自动生成的。当我们写一个构造函数的时候,程序会自动添加:构造函数名.prototype.constructor = 构造函数名

     

    function Aaa(){

    }

    //Aaa.prototype.constructor = Aaa; //每一个函数都会有的,都是自动生成的

    //Aaa.prototype.constructor = Aaa;

     

    备注:使用Object.create()创建的js对象,没有constructor

     

    4、Object.prototype.toString(可以说不管是什么类型,它都可以立即判断出)

     

    toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型

     

    格式为[object xxx],xxx是具体的数据类型,其中包括:

     

    String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。

     

    var str = 'hello';

    console.log(Object.prototype.toString.call(str));//[object String]

    var bool = true;

    console.log(Object.prototype.toString.call(bool))//[object Boolean]

    var num = 123;

    console.log(Object.prototype.toString.call(num));//[object Number]

    var nul = null;

    console.log(Object.prototype.toString.call(nul));//[object Null]

    var und = undefined;

    console.log(Object.prototype.toString.call(und));//[object Undefined]

    var oDate = new Date();

    console.log(Object.prototype.toString.call(oDate));//[object Date]

    var json = {};

    console.log(Object.prototype.toString.call(json));//[object Object]

    var arr = [];

    console.log(Object.prototype.toString.call(arr));//[object Array]

    var reg = /a/;

    console.log(Object.prototype.toString.call(reg));//[object RegExp]

    var fun = function(){};

    console.log(Object.prototype.toString.call(fun));//[object Function]

    var error = new Error();

    console.log(Object.prototype.toString.call(error));//[object Error]

     

    ####如何准确的判断数组类型:

     

    除了以上四种还有

     

    ES5定义了Array.isArray:

    Array.isArray([]) //true

     

    ###9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

     

    ####可能发生隐式类型转换的场景以及转换原则:

     

    转换原则

    值   转数字 转字符串    转布尔值

    undefined   NaN “undefined” false

    null    0   “null”  false

    true    1   “true”  

    false   0   “false” 

    0       “0” false

    -0      “0” false

    NaN     “NaN”   false

    Infinity        “Infinity”  true

    -Infinity       ”-Infinity” true

    1(非零)       “1” true

    {}(任意对象)    见下文 见下文 true

    [](任意数组)    0   ””  true

    [9](包含一个数字元素)   9   “9” true

    [”a”](其他数组) NaN 使用.join()方法 true

    function(){}(任意函数)  NaN 见下文 true

     

    1.运算符的转换:-,*,/,%会将操作数转换为数字去计算,但+不一样,两边纯数字会按数字相加,纯字符串会拼接,但数字和字符串也会将字符串和数字拼接起来。

     

    console.log("1 - '2'");

    console.log(1 - '2'); //-1

    console.log("1 * '2'");

    console.log(1 * '2'); //2

     

    2.双等号的隐式转换:

    1) 双等号两边只要有以便是NaN,便返回false,且他自身不相等

     

    console.log("NaN == 1");

    console.log(NaN == 1); //false

    console.log("NaN == NaN");

    console.log(NaN == NaN);//false

    console.log("undefined == NaN");

    console.log(undefined == NaN);//false

     

    2) 布尔值会转换为数字,false转换为0,true转换为1

    console.log('0 == false');

    console.log(0 == false); //true

    console.log('1 == true');

    console.log(1 == true); // true

     

    3) 对象的转换

    var a = [];

    var b = [];

    var c = {};

    var d = {};

    console.log("[] == []");

    console.log(a == b); // false

    console.log("[] == {}");

    console.log(a == c); // false

    console.log("{} == {}");

    console.log(d == c); // false

    console.log("[] == ![]");

    console.log(a == !b); // true

     

    解析:

     

    对于前三个的原理是一样的,当两个值都是对象 (引用值) 时, 比较的是两个引用值在内存中是否是同一个对象. 因为此 [] 非彼 [], 虽然同为空数组, 确是两个互不相关的空数组, 所以为false。

    而最后一个是因为右边空数组会转化为true,取反变为false,false变为0;左边空数组变为空字符串再变为0,0==0就为true。

     

    ####如何避免或巧妙应用

     

    把字符串类型转换为数字

    let i = '1'

    console.log(typeof(+i))

     

    ###10.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法

     

    ####出现小数精度丢失的原因

     

    0.1 + 0.2 != 0.3 // true

     

    计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。

     

    0.02625=0.000001101(二进制),无法精确求出二进制表示,因此采用“四舍五入法”(逢1进,逢0舍)。

     

    以上就是问题产生的原理,很多编译型语言如Java,c#等都对浮点数的处理进行了封装。因此平时大多数情况下,并不会出现明显的可见的问题。js本身作为解释性语言,好像这点上有着天生的劣势。

     

    浮点数,比如

     

    0.1 >> 0.0001 1001 1001 1001…(1001无限循环)

    0.2 >> 0.0011 0011 0011 0011…(0011无限循环)

     

    此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。

     

    大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。                                                大于 9007199254740992 的可能会丢失精度

     

    9007199254740992 >> 10000000000000...000 // 共计 53 个 0

    9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0

    9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0

    9007199254740992 + 1 // 丢失 //9007199254740992

    9007199254740992 + 2 // 未丢失 //9007199254740994

    9007199254740992 + 3 // 丢失 //9007199254740992

    9007199254740992 + 4 // 未丢失 //9007199254740996

     

    ####JavaScript可以存储的最大数字、最大安全数字

     

    js的number类型有个最大值(安全值)。即2的53次方,为9007199254740992。如果超过这个值,那么js会出现不精确的问题。这个值为16位。

     

    ####JavaScript处理大数字的方法、避免精度丢失的方法

     

    解决方案:

     

    对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。

     

    对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)

     

    最新回复(0)