前端工程师自检清单

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

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

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

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

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

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

7. null和 undefined的区别

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

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

1. JavaScript规定了几种语言类型(推荐阅读 https://www.cnblogs.com/onepixel/p/5140944.html

答:JavaScript有7大基本类型。(6种原始类型,1种引用类型) https://www.cnblogs.com/memphis-f/p/11913756.html

  1)、Undefined

    undefined表示未定义,它的值只有一个:undefined。当声明的变量未初始化时,变量的默认值是undefined

    任何变量赋值前都是undefined类型,值为undefined而不是null,undefined是一个变量而不是一个关键字。

    我们一般不会把变量赋值为undefined,这样可以保证所有值为undefined的变量,都是从未赋值的自然变量。

  2)、Null

    只有一个值就是null,表示空值,是关键字。

    null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象

  3)、Boolean  

Boolean(true) // true
Boolean(false) // false
Boolean('Hello Wolrd') // true
Boolean() // false
Boolean('') // false
Boolean(' ') // true (里面有空格)
Boolean(1) // true
Boolean(0) // false
Boolean(NaN) // false
Boolean({}) // true
Boolean([]) // true
Boolean(null) // false
Boolean(undefined) // false

    把其他类型转换为Boolean类型有三种方式:

    1)Boolean()

    2)! 或者 !!,取反,先转为Boolean然后再取反

    3)条件判断    

  4)、String

    字符串是存储字符的变量。

    在JS中的字符串需要用引号引起来(英文单引号或者双引号)

    String对象方法:

    1)indexOf()

      该方法可返回某个指定字符串值在字符串中首次出现的位置。并返回第一次出现的位置。如果要检索的字符串值没有出现,则返回-1

    2)lastIndexOf()

      该方法可返回某个指定字符串值在字符串中最后出现的位置,在一个字符串中的指定位置从后向前检索。如果要检索的字符串值没有出现,则返回-1

    3)concat()

      该方法用于连接两个或多个字符串。使用 + 号运算符来进行字符串的连接运算通常会更简便些。

    4)slice()

      该方法可提取字符串的某个部分,并以返回被提取的部分

    5)toLowerCase()

      该方法用于将字符串转换为小写

    6)toUpperCase()

      该方法用于将字符串转换为大写

  5)、Number

    Number是与数字值对应的引用类型

    常用方法:

    1)toFixed()

      方法会按照指定的小数位返回数值的字符串表示

    2)toExponential()

      该方法返回以指数表示法(也称 e 表示法)表示的数值的字符串形式

    3)toPrecision()

      方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看哪种格式最合适。

      这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。

var num = 10;
alert(num.toFixed(2)); //"10.00" var num = 10;
alert(num.toExponential(1)); //"1.0e+1" var num = 99;
alert(num.toPrecision(1)); //"1e+2"
alert(num.toPrecision(2)); //"99"
alert(num.toPrecision(3)); //"99.0"

  6)、Symbol

    表示独一无二的值,它是一切非字符串的对象key的集合。 Symbol 值通过Symbol函数生成。

    这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。

    凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

    Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,但是即使描述相同,Symbol值也不相等。

let s1 = Symbol('foo'); let s2 = Symbol('foo');

s1 === s2 // false

一些标准中提到的 Symbol,可以在全局的 Symbol 函数的属性中找到。例如,我们可以使用 Symbol.iterator 来自定义 for…of 在对象上的行为: var o = new Object

o[Symbol.iterator] = function() {
var v = 0
return {
next: function() {
return { value: v++, done: v > 10 }
}
}
};

for(var v of o)
console.log(v); // 0 1 2 3 ... 9

  7)、Object

    对象是某个特定引用类型的实例。在ECMAScript中,object类型是所有它的实例的基础。换句话说,Object类型所具有的任何属性和方法也同样存在于更具体的对象中。
    Object的每个实例都具有下列的属性和方法:

  • [x] constructor: 构造函数
  • [x] hasOwnProperty(propertyName)
    用于检查给定的属性在当前对象实例(而不是实例的原型)中是否存在。

  • [x] isPrototypeOf(Object):
    用于检查其原型链的对象是否存在于指定对象的实例中,是则返回true,否则返回false。
    例如:

    var a = {}
    function Person() {}
    var p1 = new Person() // 继承自原来的原型,但现在已经无法访问
    var Person.prototype = a
    var p2 = new Person() // 继承a console.log(a.isPrototypeOf(p1)) // false a是不是p1的原型
    console.log(a.isPrototypeOf(p2)) // true a是不是p2的原型 console.log(Object.prototype.isPrototypeOf(p1)) // true
    console.log(Object.prototype.isPrototypeOf(p2)) // true
  • [x] propertyIsEnumerable(propertyName)
    用于检查给定的属性是否可以用 for-in 语句进行枚举。
  • [x] toLocaleString()
    返回对象的字符串表示,该字符串与执行环境的地区对应。
  • [x] toString()
    返回对象的字符串表示。
  • [x] valueOf()
    返回对象的字符串、数值、布尔值表示。通常与toString()方法的返回值相同。
    创建Object实例的方法:
    1、第一种是使用new操作符后跟Object构造函数。
    var person=new Object();
    person.name="Nicholas";
    person.age=29;

    2、使用对象字面量。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。

    var person={
    name:"Nicholas",
    age:29
    };

2. JavaScript对象的底层数据结构是什么
参考文章:再谈JS 对象数据结构底层实现原理

     从Chrome源码看JS Object的实现

3. Symbol类型在实际开发中的应用、可手动实现一个简单的 Symbol(参考文档

答:Symbol是由ES6规范引入的一项新特性,它的功能类似于一种标识唯一性的ID。每个Symbol实例都是唯一的。

  因此,当你比较两个Symbol实例的时候,将总会返回false

let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
s1 === s2 // false
s2 === s3 // false

  应用场景1:使用symbol来作为对象属性名(key)

// ① 通常我们定义或访问对象的属性时都是使用字符串
let obj = {
abc: 123,
"hello": "world"
}
obj["abc"] //
obj["hello"] // 'world'
// ② 现在symbol同样可以用于对象属性的定义和访问
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
[PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18
obj[PROP_NAME] // '一斤代码'
obj[PROP_AGE] //
// ③ Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。如下:
let obj = {
[Symbol('name')]: '一斤代码',
age: 18,
title: 'Engineer'
}
Object.keys(obj) // ['age', 'title'] for (let p in obj) {
console.log(p) // 分别会输出:'age' 和 'title'
}
Object.getOwnPropertyNames(obj) // ['age', 'title']
// ④ 也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外:
JSON.stringify(obj) // {"age":18,"title":"Engineer"}
// ⑤ 我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。
// ⑥ 获取以symbol方式定义对象属性的API
// 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']

  应用场景2:使用symbol来代替常量

// 我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间是唯一的关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),
常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字。如下:
const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'
function handleFileResource(resource) {
switch(resource.type) {
case TYPE_AUDIO:
playAudio(resource)
break
case TYPE_VIDEO:
playVideo(resource)
break
case TYPE_IMAGE:
previewImage(resource)
break
default:
throw new Error('Unknown type of resource')
}
}
// 现在有了Symbol,我们大可不必这么麻烦了,这样定义,直接就保证了三个常量的值是唯一的了:
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()

  应用场景3:使用Symbol定义类的私有属性/方法  

// 在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。
而有了Symbol以及模块化机制,类的私有属性和方法才变成可能。例如:
在文件 a.js中:
const PASSWORD = Symbol(
class Login {
constructor(username, password) {
this.username = username
this[PASSWORD] = password
}
checkPassword(pwd) {
return this[PASSWORD] === pwd
}
}
export default Login
在文件 b.js 中:
import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456') // true
login.PASSWORD // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!
// 由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),
// 因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。

4. JavaScript中的变量在内存中的具体存储形式(参考文档

答:JavaScript中的变量分为基本类型和引用类型

  基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问

  引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用

  基本类型发生复制行为:在栈内存中的数据发生复制行为时,系统会自动为新的变量分配一个新值,最后这些变量都是相互独立互不影响的 

  引用类型发生复制行为:

  • 引用类型的复制,同样为新的变量b分配一个新的值,保存在栈内存中,不同的是,这个值仅仅是引用类型的一个地址指针
  • 他们两个指向同一个值,也就是地址指针相同,在堆内存中访问到的具体对象实际上是同一个
  • 因此改变b.x时,a.x也发生了变化,这就是引用类型的特性

  

  总结:
  

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

答:基本类型对应的内置对象:String()、Number()、Boolean()、RegExp()、Date()、Error()、Array()、

  Function()、Object()、symbol();类似于对象的构造函数

  1、这些内置函数构造的变量都是封装了基本类型值的对象如:

  Var a=new String(‘abb’); //typeof(a)=object

  除了利用Function()构造的变量通过typeof输出为function外其他均为object

  2、为了知道构造的变量的真实类型可以利用:

  Object.prototype.toString.call([1,2,3]);//”[object,array]”,后面的一个值即为传入参数的类型

  3、如果有常量形式(即利用基本数据类型)赋值给变量就不要用该方式来定义变量

  一、装箱

  所谓装箱,就是把基本类型转变为对应的对象。装箱分为隐式和显式:

  隐式

// 每当读取一个基本类型的值时,后台会创建一个该基本类型所对应的对象。在这个基本类型上调用方法,其实是在这个基本类型对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。具体到代码如下:
num.toFixed(2); // '123.00'
// 上方代码在后台的真正步骤为
var c = new Number(123);
c.toFixed(2);
c = null;
// 当我们访问 num 时,要从内存中读取这个数字的值,此时访问过程处于读取模式。在读取模式中,后台进行了三步处理:
/**
1、创建一个 Number 类型的实例。
2、在实例上调用方法。
3、销毁实例。
**/

  显式

// 通过内置对象 Boolean Object String等可以对基本类型进行显式装箱
var obj = new String('123');

  二、拆箱

  拆箱与装箱相反,把对象转变为基本类型的值。

// 拆箱过程内部调用了抽象操作ToPrimitive:
// 该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。
ToPrimitive('要转变的对象', PreferredType) {
// 操作方法 ........
}
// 第二个参数不是必须的,默认该参数为 number,即对象被期待转为数字类型。有些操作如 String(obj) 会传入 PreferredType 参数。有些操作如 obj + " " 不会传入 PreferredType。
// 具体转换过程是这样的:
// ① 默认情况下,ToPrimitive 先检查对象是否有 valueOf 方法,如果有则再检查 valueOf 方法是否有基本类型的返回值;
// ② 如果没有 valueOf 方法或 valueOf 方法没有返回值,则调用 toString 方法;
// ③ 如果 toString 方法也没有返回值,产生 TypeError 错误。
// PreferredType 影响 valueOf 与 toString 的调用顺序。如果 PreferrenType 的值为 string。则先调用 toString ,再调用 valueOf。
// 具体测试代码如下:
var obj = {
valueOf : () => {console.log("valueOf"); return []},
toString : () => {console.log("toString"); return []}
}
String(obj)
// toString
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value
obj+' '
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value
Number(obj)
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value

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

答:JavaScript中的变量类型:

  (1) 值类型(基本类型):字符串(string)、数值(number)、布尔值(boolean)、undefined、null  (这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值)

    (ECMAScript 2016新增了一种基本数据类型:symbolhttp://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” ,所以可以认为它是一个特殊的对象值。表示"没有对象",即该处不应该有值。用法:


  Object.getPrototypeOf(Object.prototype)
  // null

(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

  undefined:Undefined类型只有一个值,即undefined。表示"缺少值",就是此处应该有一个值,但是还没有定义。用法:

(1)变量被声明了,但没有赋值时,就等于undefined。(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。(3)对象没有赋值的属性,该属性的值为undefined。(4)函数没有返回值时,默认返回undefined。
知识了解:
undefined与null的区别,这与JavaScript的历史有关。1995年JavaScript诞生时,最初像Java一样,只设置了null作为表示"无"的值。
根据C语言的传统,null被设计成可以自动转为0。
Number(null)
// 0
5 + null
// 5
但是,JavaScript的设计者Brendan Eich,觉得这样做还不够,有两个原因。
首先,null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示"无"的值最好不是对象。
其次,JavaScript的最初版本没有包括错误处理机制,发生数据类型不匹配时,往往是自动转换类型或者默默地失败。Brendan Eich觉得,如果null自动转为0,很不容易发现错误。
因此,Brendan Eich又设计了一个undefined。

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

答:判断数据类型的方法一般可以通过:typeof、instanceof、constructor、toString四种常用方法:

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

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

    typeof 可以对JS基本数据类型做出准确的判断(除了null),而对于引用类型返回的基本上都是object,。

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

    注意:typeof  null会返回object,因为特殊值null被认为是一个空的对象引用

  2、instanceof

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

function Aaa(){}
var a1 = new Aaa();
//alert( a1 instanceof Aaa); //true 判断a1和Aaa是否在同一个原型链上,是的话返回真,否则返回假
var arr = [];
alert( arr instanceof Aaa);//false
看一下下面的判断:
var str = 'hello';
alert(str instanceof String);//false
var bool = true;
alert(bool instanceof Boolean);//false
var num = 123;
alert(num instanceof Number);//false
var nul = null;
alert(nul instanceof Object);//false
var und = undefined;
alert(und instanceof Object);//false
var oDate = new Date();
alert(oDate instanceof Date);//true
var json = {};
alert(json instanceof Object);//true
var arr = [];
alert(arr instanceof Array);//true
var reg = /a/;
alert(reg instanceof RegExp);//true
var fun = function(){};
alert(fun instanceof Function);//true
var error = new Error();
alert(error instanceof Error);//true
从上面的运行结果我们可以看到,基本数据类型是没有检测出他们的类型,但是我们使用下面的方式创建num、str、boolean,是可以检测出类型的:
var num = new Number(123);
alert(num instanceof Number);//true
var str = new String('abcdef');
alert(str instanceof String);//true
var boolean = new Boolean(true);
alert(boolean instanceof Boolean;//true

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

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

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

    判断数据类型的方法

var str = 'hello';
alert(str.constructor == String);//true
var bool = true;
alert(bool.constructor == Boolean);//true
var num = 123;
alert(num.constructor ==Number);//true
// var nul = null;
// alert(nul.constructor == Object);//报错
//var und = undefined;
//alert(und.constructor == Object);//报错
var oDate = new Date();
alert(oDate.constructor == Date);//true
var json = {};
alert(json.constructor == Object);//true
var arr = [];
alert(arr.constructor == Array);//true
var reg = /a/;
alert(reg.constructor == RegExp);//true
var fun = function(){};
alert(fun.constructor ==Function);//true
var error = new Error();
alert(error.constructor == Error);//true
// 从上面的测试中我们可以看到,undefined和null是不能够判断出类型的,并且会报错。因为null和undefined是无效的对象,因此是不会有constructor存在的
// 同时我们也需要注意到的是:使用constructor是不保险的,因为constructor属性是可以被修改的,会导致检测出的结果不正确
function Aaa(){}
Aaa.prototype.constructor = Aaa;//程序可以自动添加,当我们写个构造函数的时候,程序会自动添加这句代码
function BBB(){}
Aaa.prototype.constructor = BBB;//此时我们就修改了Aaa构造函数的指向问题
alert(Aaa.construtor==Aaa);//false
// 可以看出,constructor并没有正确检测出正确的构造函数

  4、Object.prototype.toString()

    toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

    对于 Object 对象,直接调用 toString()  就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

    判断类型example:

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用

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

答:1、运算符转换

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

console.log("1 - '2'");
console.log(1 - '2'); //-1
console.log("1 * '2'");
console.log(1 * '2'); //
console.log("6 / '4'");
console.log(6 / '4'); //1.5
console.log("6 % '4'");
console.log(6 % '4'); //2

console.log("6 + 4");
console.log(6 + 4); //
console.log("6 + '4'");
console.log(6 + '4'); //
console.log("'6' + '4'");
console.log('6' + '4'); //
console.log("typeof'6' + '4'");
console.log(typeof('6' + '4')); //string

  2、双等号转换

    两边会转换为同一类型再进行比较。双等号两边只要有以便是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

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

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

    对象的转换

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。

  3、· 点号操作符

    数字、字符串等直接做·操作调用方法时,隐式地将类型转换成对象。

  4、if()语句

    括号里的表达式部分会被隐式转换为布尔类型进行判别

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

答:精度丢失:

  因为有些小数在计算机使用二进制方式表示时无法准确的表示出来,类似于十进制中的1/3一样,无理数,无限循环.

  可是计算机存储小数的类型不管是float还是double都是有位数限制的,所以他们存储的只是一个近似值,这就导致了精度丢失.

  解决办法:

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

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

// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3 // true

一位网友写的方法,对小数的加减乘除丢失精度做了屏蔽,换算后的整数不能超过 9007199254740992:

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

/**
* floatObj 包含加减乘除四个方法,能确保浮点数运算不丢失精度
*
* 我们知道计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是二进制和实现位数限制有些数无法有限表示
* 以下是十进制小数对应的二进制表示
* 0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
* 0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
* 计算机里每种数据类型的存储是一个有限宽度,比如 JavaScript 使用 64 位存储数字类型,因此超出的会舍去。舍去的部分就是精度丢失的部分。
*
* ** method **
* add / subtract / multiply /divide
*
* ** explame **
* 0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004)
* 0.2 + 0.4 == 0.6000000000000001 (多了 0.0000000000001)
* 19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002)
*
* floatObj.add(0.1, 0.2) >> 0.3
* floatObj.multiply(19.9, 100) >> 1990
*
*/
var floatObj = function() { /*
* 判断obj是否为一个整数
*/
function isInteger(obj) {
return Math.floor(obj) === obj
} /*
* 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
* @param floatNum {number} 小数
* @return {object}
* {times:100, num: 314}
*/
function toInteger(floatNum) {
var ret = {times: 1, num: 0}
var isNegative = floatNum < 0
if (isInteger(floatNum)) {
ret.num = floatNum
return ret
}
var strfi = floatNum + ''
var dotPos = strfi.indexOf('.')
var len = strfi.substr(dotPos+1).length
var times = Math.pow(10, len)
var intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10)
ret.times = times
if (isNegative) {
intNum = -intNum
}
ret.num = intNum
return ret
} /*
* 核心方法,实现加减乘除运算,确保不丢失精度
* 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
*
* @param a {number} 运算数1
* @param b {number} 运算数2
* @param digits {number} 精度,保留的小数点数,比如 2, 即保留为两位小数
* @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
*
*/
function operation(a, b, digits, op) {
var o1 = toInteger(a)
var o2 = toInteger(b)
var n1 = o1.num
var n2 = o2.num
var t1 = o1.times
var t2 = o2.times
var max = t1 > t2 ? t1 : t2
var result = null
switch (op) {
case 'add':
if (t1 === t2) { // 两个小数位数相同
result = n1 + n2
} else if (t1 > t2) { // o1 小数位 大于 o2
result = n1 + n2 * (t1 / t2)
} else { // o1 小数位 小于 o2
result = n1 * (t2 / t1) + n2
}
return result / max
case 'subtract':
if (t1 === t2) {
result = n1 - n2
} else if (t1 > t2) {
result = n1 - n2 * (t1 / t2)
} else {
result = n1 * (t2 / t1) - n2
}
return result / max
case 'multiply':
result = (n1 * n2) / (t1 * t2)
return result
case 'divide':
result = (n1 / n2) * (t2 / t1)
return result
}
} // 加减乘除的四个接口
function add(a, b, digits) {
return operation(a, b, digits, 'add')
}
function subtract(a, b, digits) {
return operation(a, b, digits, 'subtract')
}
function multiply(a, b, digits) {
return operation(a, b, digits, 'multiply')
}
function divide(a, b, digits) {
return operation(a, b, digits, 'divide')
} // exports
return {
add: add,
subtract: subtract,
multiply: multiply,
divide: divide
}
}();

前端知识体系:JavaScript基础-变量和类型的更多相关文章

  1. Web前端知识体系精简

    Web前端技术由html.css和javascript三大部分构成,是一个庞大而复杂的技术体系,其复杂程度不低于任何一门后端语言.而我们在学习它的时候往往是先从某一个点切入,然后不断地接触和学习新的知 ...

  2. 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!

    前言 见解有限,如有描述不当之处,请帮忙指出,如有错误,会及时修正. 为什么要梳理这篇文章? 最近恰好被问到这方面的问题,尝试整理后发现,这道题的覆盖面可以非常广,很适合作为一道承载知识体系的题目. ...

  3. Web前端知识体系

    看到一篇不错的文章,拿来收藏和分享. 原文:http://mp.weixin.qq.com/s/UFTfdE7LYhHquWEzwZKLCQ Web前端技术由html.css和 javascript三 ...

  4. web前端知识体系总结

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  5. 自己总结的web前端知识体系大全【欢迎补充】

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  6. web前端知识体系大全

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  7. web前端知识体系小结(转)

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  8. web前端知识体系大全【欢迎补充】

    大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的是想要颠覆人 ...

  9. web前端知识体系大全【转载】

    自己总结的web前端知识体系大全[欢迎补充]   1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在 ...

随机推荐

  1. Linux中 cmake-3.x 编译安装以及man page添加

    首先回顾一下 cmake-2.x 的编译安装. ================ cmake-2.x编译安装说明 ================编译安装的命令: ./bootstrap --pref ...

  2. when i import skimage,it occurred --"cannot import name '_validate_lengths'"

    how to sovle this prolem? 1)with the administrator user to run cmd 2)imput and run : pip install --u ...

  3. [百家号]7nm ARM 64核!华为Hi1620高性能CPU公开:3.0GHz

    7nm ARM 64核!华为Hi1620高性能CPU公开:3.0GHz https://baijiahao.baidu.com/s?id=1617735663824201180&wfr=spi ...

  4. 小菜鸟之SSM框架

    # SSM框架 # **什么是框架** 就是模板,将一些基础性的程序和代码由框架模板提供,然后程序员补充和业务需求相关的代码. # **ssm框架组成** s: springMvc 子框架 代替ser ...

  5. 【AtCoder】M-SOLUTIONS Programming Contest

    M-SOLUTIONS Programming Contest A - Sum of Interior Angles #include <bits/stdc++.h> #define fi ...

  6. (四)mybatis 的主键返回

    目录 文章目录 自增主键(LAST_INSERT_ID()) 非自增主键(UUID() ) 自增主键(LAST_INSERT_ID()) 在映射关系文件中配置 <!--插入用户--> &l ...

  7. 剑指offer19:按照从外向里以顺时针的顺序依次打印出每一个数字,4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

    1 题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印 ...

  8. codeforces 1251D Salary Changing (二分+贪心)

    (点击此处查看原题) 题意分析 一共有s元钱,要用这些钱给n个人发工资,发给每个人的工资si有最少和最多限制 si ∈[li,ri],在发给n个人的总工资小于s的情况下,要求发给n个人中的工资的中位数 ...

  9. 小木棒HDU1455(DFS+剪枝)

    题意:HDU1455 给出n个小木棒的长度,他们是又数根长度相同的木棒剪短得来的,问没剪短之前的木棒长度最短是多少. 思路: 见代码:https://www.cnblogs.com/fqfzs/p/9 ...

  10. gdb暂停或恢复程序的运行

    ref : https://blog.csdn.net/seu_lyr/article/details/9050657   一 暂停程序的运行: (一)GDB的暂停方式:断点(BreakPoint). ...