你不知道的JS读书笔记

上卷

eval(...) 和 with:

js中有两个机制可以 " 欺骗 " ,词法作用域,eval 和 with。

前者可以对一段包含一个或多个声明的 " 代码 " 字符串进行演算,并借此来修改已经存在的词法作用域 ( 在运行时 )

后者本质上是通过将一个对象的引用当作 作用域 来处理,将对象的属性当作作用域中的标识符处理,从而创建了一个新的词法作用域 (同样是在运行时)

 

函数作用域的含义:属于这个函数的全局变量都可以在整个函数的范围内使用以及复用。

函数声明和函数表达式之间最重要的区别:是它们的名称标识符将会绑定到何处。

 

匿名函数的缺点:

①匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。

②没有函数名,当函数需要引用自身时只能使用已经过期的 arguments.callee 引用。(举例:递归中,事件触发后事件监听器需要解绑自身)

③匿名函数省略了对于代码 可读性 / 可理解性 很重要的函数名。(一个描述性的名称可以让代码的功能不言而喻)

 

IIFE:立即执行函数表达式,Immediately Invoked Function Exprssion ;(举例:( function foo (){...} )() 。第一个()将函数变为表达式,第二个执行了这个函数,另一种写法( function foo (){...} ()) 。二者功能一致)

 

块级作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息扩展为在块中隐藏信息。

变量的声明应该距离使用的地方越近越好,并最大限度地本地化。

 

let:let关键字可以将变量绑定到所在的任意作用域中(通常是{...}内部)。换句话说,let为其声明的变量隐式地劫持了所在的块作用域。(只要声明是有效的,在声明中任意位置都可以使用 {...} 括号来为let创建一个用于绑定的块,{} 即显示的。)

 

从ES3开始,try / catch 结构在 catch 分句中具有块级作用域。

 

闭包是基于词法作用域书写代码时所产生的的自然结果。

 

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

 

在定时器,事件监听器,Ajax请求,跨窗口通信,Web Workers 或者 任何其他的异步(或同步)任务中,只要使用了回调函数,实际上就是在使用闭包!

 

当函数可以记住并访问所在的词法作用域,即使函数时在当前词法作用域之外执行,这时就产生了闭包。(作用域就是词法作用域,词法作用域是一套关于引擎如何寻找变量以及会在何处找到变量的规则。词法作用域最重要的特征是它的定义过程发生在代码的书写阶段。

 

模块有两个主要特性:①为创建内部作用域而调用了一个包装函数;②包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。

 

作用域链是基于调用栈的,而不是代码中的作用域嵌套。(动态作用域不关系函数和作用域是如何声明以及何处声明的,只关心它们从何处调用。this机制息息相关,二者是表亲。)

 

箭头函数在设计 this 绑定时的行为和普通函数的行为完全不一致。它放弃了所有普通 this 绑定的规则,取而代之的是用当前的词法作用域覆盖了 this 本来的值。

 

为什么要用this:

随着使用的模式越来越复杂,显示传递上下文对象会让代码变得越来越混乱,this 提供了一种更优雅的方式来"传递"一个对象引用。

 

this是什么:

this是在运行时进行绑定的,并不是编写时绑定它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。this就是这个记录的一个属性。

 

如何判断this:

可以根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照以下顺序判断:

①函数是否在 new 中调用(new绑定)?如果是的话this绑定的是新创建的对象。  // var bar = new foo()

②函数是否通过call,apply(显示绑定,或者bind)或者应绑定调用?如果是的话,this绑定的是指定的对象。 // var bar = foo.call(obj2)

③函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。 // var bar = obj1.foo()

④如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined , 否则绑定到全局对象。 // var bar = foo()

ps:如果要判断一个运行中函数的this绑定,就需要找到这个函数直接调用的位置。找到后就可以用这四条规则来判断this绑定的对象。

有些调用可能在无意中使用默认绑定规则,如果想“更安全”地忽略this绑定,你可以使用一哥DMZ对象,比如 θ=Object.create(null) ,以保护全局对象。

 

对象可以通过两种形式定义:声明(文字)形式 和 构造形式。

//文字语法:
var myObj = {
      key : value ,
      //...
}

//构造形式:
var myObj = new object();
myObj.key = value;

//二者唯一的区别就是文字声明中可以一次添加多条,构造形式中必须逐条添加。

 

函数就是对象的一个子类型(技术角度上是 " 可调用对象 ")。JS中函数时 " 一等公民 " ,因为它们本质上和普通的对象一样,只是可以调用,所以可以像操作其他对象一样操作函数。

数组也是对象的一种类型,具备一些额外的行为。数组中内容的组织方式比一般的对象要稍微复杂一些。

 

ps:null是基本类型,虽然 typeof null 返回 object 。 原因:不同的对象底层都表示为二进制,在js中二进制前三位都为0的话会被判断为object类型,null的二进制表示是全0,自然前三位也是0,所以执行 typeof 时会返回 " object " 。

属性访问:myObject.a;

键访问:myObject[a];

在对象中属性名永远都是字符串。

ES6增加了可以计算属性名,可以在文字形式中使用 [ ] 包裹一个表达式当做属性名。

 

一种巧妙的对象复制:var  newObj = JSON.parse( JSON.stringify( someObj ) ) ;

ES6定义了浅复制对象方法:Object.assign(...);

 

从ES开始,所有属性都具备了属性描述符:

 

var myObj = {  a : 7 }

Object.getOwnPropertyDescriptor( myObj ,a ) ;

{

  value:7,

  writable: true// 决定是否可以修改属性的值

  enumerable: true ,

  configurable: true// 属性是否可配置

}

// 结合 writable:false 和 configurable:false 就可以创建一个真正的常量属性。 

  

禁止扩展:Object.preventExtensions(...) ; 非严格模式下创建属性会失败,严格模式 TypeError。

密封:Object.seal(...) ; 不能新增属性,同时不能重新配置或删除任何现有属性。

冻结:Object.freeze(...) ;会在一个现有对象上调用Object.seal 并把所有 " 数据访问 " 属性标记为 writable : false ;这个方法是你可以在对象上的级别最高的不可变性,它会禁止对象本身及其任意直接属性的修改。

 

在ES5中可以使用 getter 和 setter 部分改写默认操作,但是只能应用在单个属性上,无法应用在整个对象上。getter 是一个隐藏函数,会在获取属性时调用。setter 也是一个隐藏函数,会在设置属性时调用

当你给一个属性定义 getter 、setter 或者两者都有时,这个属性会被定义为 " 访问描述符 " (和 "  数据描述符相对 ")。对于访问描述符来说,JS会忽略它的 value 和 writable 特性,取而代之的是关心 set 和 get (还有 configurable 和 enumerable)特性。

 

存在性:

in 操作符会检查属性是否在对象及其 [[Prototype]] 原型链中。相比之下 hasOwbPropertype(...) 只会检查属性是否在对象中,不会检查 [[Prototype]] 链。

所有的普通对象都可以通过对于 Object.prototype 的委托来访问 hasOwnPropertype(...) ;如果对象没有连接到 Object.prototype(通过 Object.create(null) 来创建)。这种情况 myObject.hasOwnProperty(...) 就会失败,可以用一种更加强硬的方法来判断:Object.prototype.hasOwnProperty.call( myObject ," a " ) ;

in 操作符 可以检查容器内 某个属性名 是否存在。

propertyIsEnumerable(..): 会检查给定的属性名是否直接存在于对象中,碧昂满足 enumerable :true;

Object.keys(..) 和 propertyIsEnumerable(..) : 都只会查找对象直接包含的属性,不会查找 [[Prototype]] ;

 

ES6 新增了一种遍历数组数组的 for...of 循环语法,它受限会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的 next() 方法来遍历所有返回值。数组有内置的 @@iterator ;

 

var myArray = [ 1 ,2 ,3 ] ;

var  it = myArray[Symbol.iterator]() ;

it.next() ; // { value:1 ,done:false } 

it.next() ; // { value:2 ,done:false } 

it.next() ; // { value:3 ,done:false } 

it.next() ; // { done:true } 

//我们使用 ES6 中的符号 Symbol.iterator 来获取对象的 @@iterator 内部属性。

 

给想遍历的对象定义 @@iterator :

var myObj = {
  a:2,
  b:3
}
Object.defineProperty(myObj, Symbol.iterator, {
  enumerable: false,
  writable: false,
  configurable: true,
  value: function() {
    var o = this;
    var idx = 0;
    var ks = Object.keys(o);
    return {
      next: function() {
        return {
          value: o[ks[idx++]],
          done: (idx > ks.length)
        }
      }
    }
  }
})

var it = myObj[Symbol.iterator]();
it.next(); // {value: 2, done: false}
it.next(); // {value: 3, done: false}
it.next(); // {done: true}


//除了使用
Object.defineProperty 也可以在定义对象时直接声明,比如 var myObj = {a:2,b:3,[Symbol.iterator]:function(){/*..*/} }

 

小结:

对象是基础类型之一。对象有包括 function 在内的子类型,不同子类型具有不同的行为,比如内部标签 [ object Array ] 表示这是对象的子类型数组。

对象就是 键 / 值 对的集合。可以通过 .propName 或者 ["propName"] 语法来获取属性值。

属性的特性可以通过属性描述符来控制。比如 writable 和 configurable 。 此外,可以使用 Object.preventExtensions(..) 、Object.seal(..) 和 Object.freeze(..)  来设置对象(及其属性)的不可变性级别。

属性不一定包含值——他们可能是具备 getter / setter 的 " 访问描述符 "。此外,属性可以是可枚举或者不可枚举的。这决定了它们是否会出现在 for..in 循环中。

你可以通过使用 ES6 的 for..of 语法来遍历数据结构(数组,对象,等等)中的值,for..of 会寻找内置或者自定义的 @@iterator 对象并调用它的 next() 方法来遍历数据值。

 

混合对象“类”:

 

面向类的设计模式:实例化,继承,多态。

面向对象变成强调的是数据和操作数据的行为本质上是互相关联的。

 

类/继承 描述了一种代码的组织结构形式——一种在软件中对真实世界中问题领域的建模方法。

你可能从来没把类作为设计模式来看待,讨论得最多的是面向对象设计模式,比如 迭代器模式、观察者模式、工厂模式、单例模式 等等。从这个角度说,我们似乎是在(低级)面向对象类的基础上实现了所有(高级)设计模式,似乎面向对象是优秀代码的基础。

 

由于js中父类和子类的关系只存在于两者构造函数对应的 .prototype 对象中,因此它们的构造函数之间并不存在直接联系,从而无法简单地实现两者的相对引用(ES6的类中可以通过 super 来解决这个问题)。

多态并不表示子类和父类有关联,子类得到的只是父类的一份副本。类的继承其实就是复制。

 

在继承和实例化时,js的对象机制并不会自动执行复制行为。简单来说,js中只有对象,并不存在可被实例化的 "类" 。一个对象并不会被复制到其它对象,它们会被关联起来。由于其它语言中类表现出来的都是复制行为,因此js开发者也想出了一个 模拟类的复制行为,这个方法就是混入:显示和隐式。一定注意,只在能提高代码可读性的前提下使用显示混入,避免使用增加代码理解难度或者让对象关系更加复杂的模式。

 

小结:

  • 类是一种设计模式,许多语言提供了对于面向类软件设计的原生语法。js也有类似的语法,但是和其他语言中的类完全不同。

  • 类意味着复制。
  • 传统类被实例化时,它的行为会被复制到实例中。类被继承时,行为也会被复制到子类中。

  • 多态(在继承链的不同层次名称相同但是功能不同的函数)看起来似乎是从子类引用父类,但本质其实是复制的结果。
  • 混入模式,可以用来模拟类的复制行为,但是通常会产生丑陋并且脆弱的语法。
  • 总地来说,在js中模拟类的行为时得不偿失的,可能解决当前问题,但是也会埋下更多隐患。

 

原型:

js中的对象有一个特殊的 [[prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有对象在创建时 [[prototype]] 属性都会被赋予一个非空的值。

Object.create(..) 会创建一个对象并把这个对象的[[Prototype]]关联到指定的对象。举例:var obj = Object.create( anotherObj ) ;

 

对象属性设置和屏蔽: myObject.foo = "AZUKI七" ;

myObject中包含的foo属性会屏蔽原型链上所有的foo属性,因为 myObject.foo 总是会选择原型链中最底层的foo属性。

如果foo不直接存在于 myObject 中而是存在于原型链上层时  myObject.foo = "AZUKI七" 会出现三种情况:

①如果在 [[prototype]] 链上存在名为 foo 的普通数据访问属性并且没有被标记为只读(writable:false),那么就会直接在myObject中添加一个名为foo的属性,它是屏蔽属性。

②如果在 [[prototype]] 链上存在 foo ,但是它被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在 严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。

③如果在 [[prototype]] 链上存在 foo 并且它是一个 setter ,那么久一定会调用这个 setter 。 foo 不会被添加到(或者说屏蔽于) myObject,也不会重新定义foo这个setter。

 

js可以不通过类,直接创建对象。js中,不存在类,对象直接定义自己的行为,js中只有对象。

所有函数默认都会拥有一个名为 prototype 的公有并且不可枚举的属性,它会指向另一个对象。

继承意味着复制操作,js默认并不会复制对象属性。相反,js会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。

 

function Foo(){
    //...
}

Foo.prototype.constructor === Foo ;  // true

var a = new Foo();
a.constructor === Foo ;  // true

//Foo.prototype 默认有一个公有并不可枚举的属性 .constructor ,这个属性应用的是对象关联的函数
//此外可以看到通过 '
构造函数’ 调用 new Foo() 创建的对象也有一个 .constructor 属性,指向 “创建这个对象的函数”
//实际上,Foo和你程序中的其它函数没有任何区别。函数本身并不是构造函数,然而,当你在普通函数调用前面加上new关键字后,就会把这个函数调用变成一个“构造函数调用”
//实际上,new 会劫持所有普通函数并用构造对象的形势来调用它。
//new 无论如何都会构造一个对象。

在js中对于构造函数最准确的解释是,所有带 new 的函数调用。

函数不是构造函数,但是当且仅当使用 new 时,函数调用,会变成 "构造函数调用" ;

 

实际上,对象的  .constrouctor 属性默认指向一个函数,而这个函数也有一个叫 .prototype 的引用指向这个对象。“构造函数”  和  “原型”  这两个默认只有松散的含义,实际值可能适用也可能不适用。最好的办法就是记住 "constructor" 并不表示(对象) 被 (它)构造。

.constructor 并不是一个不可变属性。它是不可枚举的,但是它的值是可写的(可以被修改)。此外,你可以给任意 [[prototype]] 链中的任意对象添加一个名为 constructor 的属性或者对其进行修改,你可以任意对其赋值。=

 

检查一个实例(js中的对象)的继承祖先(js中的委托关联)通常被称为内省(或者反射);

如何通过内省找出 a 的 “祖先” (委托关联)呢?

function Foo(){
    // ...
}

Foo.prototype.blah = ... ;

var a = new Foo();

第一种:站在类的角度来判断 a instanceof Foo ; // true

instanceof 操作符的左操作数是一个普通对象,右操作数是一个函数。instanceof 回答的问题是 : 在 a 的整条 [[prototype]] 链中是否有 Foo.prototype 指向的对象、

这种方法,只能处理 对象(a) 和 函数(带 .prototype 引用的 Foo)之间的关系。两个对象就不可以了。

tips:如果使用内置的 .bild(...) 函数来生成一个硬绑定函数的话,该函数是没有 .prototype 属性的。在这样的函数上使用 instanceof 的话,目标函数的 .prototype 会代替硬绑定函数的 .prototype 。通常我们不会在 “构造函数调用” 中使用硬绑定函数,不过如果你这么做了的话,实际上相当于直接调用目标函数。同理,在硬绑定函数上使用 instanceof 也相当于直接在目标函数上使用 instanceof 。

第二种:Foo.prototype.isPrototypeOf(a) ; // true

注意,在本例中,我们手撕鸡上并不关心(甚至不需要)Foo,我们只需要一个可以用来判断的对象(本例中是 Foo.prototype)就行。isPrototypeOf(...) 回答的问题是:在 a 的整条 [[Prototype]] 链中是否出现过 Foo.prototype?

 

同样的问题,同样的答案,但是在第二种方法中并不需要间接引用函数(Foo),它的 .prototype 属性会被自动访问。

我们只需要两个对象就可以判断它们之间的关系。举例来说:

// b是否出现在c的 [[Prototype]] 链中?

b.isPrototypeOf( c ) ;

它直接使用 b 和 c 之间的对象引用用来判断它们的关系。

 

最好把 [[Prototype]] 对象关联看作是只读特性。JS社区中对于双下划线有一个非官方的称呼,他们会把 __proto__ 的属性称之为 “笨蛋(dunder)” 。所以,js潮人会把 __proto__ 叫做 “ 笨蛋 proto ” 。

我们知道了,[[Prototype]] 机制就是存在于对象中的一个内部链接,它会引用其他对象。通常来说,这个链接的作用是:如果对象上没有找到需要的属性或者方法引用,引擎就会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的引用就会继续查找它的 [[Prototype]] ,以此类推。这一系列对象的链接被称为“原型链”。

 

为什么说 Object.create(...) 是一个大英雄?

var foo ={
      something:function(){
          console.log("AZUKINANA LOVE BOZAI")
      }
}

var bar = Object.create( foo );

bar.something(); //AZUKINANA LOVE BOZAI

Object.create(...) 会创建一个新对象 (bar) 并把它关联到我们指定的对象 (foo) ,这样我们就可以充分发挥 [[Prototype]] 机制的威力(委托)并且避免不必要的麻烦(比如使用new的构造函数调用会生成 .prototype 和 .constructor 引用)。

Object.create(null) 会创建一个拥有空(或者说 null)[[Prototype]] 链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以 instanceof 操作符无法进行哦安短,因此总会返回false。这些特殊的空 [[Prototype]] 对象通常被称作 “字典” ,它们完全不会受到原型链的干扰,因此很适合用来存储数据。

//Object.create(...) 是ES5中新增的函数,所以在ES5之前的函数想要支持这个功能的话就需要使用一段简单的 polyfill 代码,它部分实现了 Object.creat(...) 的功能。

if(!Object.create){
   Object.creat = function(o){
       function F(){}
       F.prototype = o;
       return new F();
   };
}

//这段 polyfill 代码使用了一个一次性函数 F,我们通过改写它的 .prototype 属性使其指向想要关联的对象,然后再使用 new F() 来构造一个新对象进行关联。

 [[Prototype]] 机制就是指对象中的一个内部链接引用另一个对象,如果第一个对象是哪个没找到需要的属性和方法,引擎就会继续在[[prototype]]关联的对象上进行查找。同理,后者中也没找到就会继续查找它的[[prototype]],以此类推。这一系列的链接被称为“原型链”。js中这个机制的本质就是对象之间的关联关系。

 

委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象。

 

如果你并不是使用“构造函数”来生成对象,比如使用对象关联风格来编写代码,那Chrome就无法跟踪对象内部的“构造函数名称”,这样的对象输出是Object {} ,意思是 “Object()构造出的对象”。当然这并不是对象关联风格代码的缺点。当你使用对象关联风格来编写代码并使用行为委托设计模式时,并不需要关注是谁“构造了”对象(就是使用new调用的那个函数)。只有使用类风格编写代码时 Chrome 内部的 “构造函数名称” 跟踪才有意义,使用对象关联时这个功能不起任何作用。

ES6中我们可以在任意对象的字面形势中使用 简洁方法声明 .

var obj = {

     geter(){

        //可以忽略function,与class语法糖不同的是,对象的字面形式还是需要使用  " , " 来分隔元素。

    },

}

 

匿名函数表达式三大缺点:

1.调试栈更难追踪。

2.自我引用(递归,事件(解除)绑定,等等)更难。

3.代码(稍微)更难理解。

 

内省就是检查实例的类型。类实例的内省主要目的是通过创建方式来判断对象的结构和功能。

 

行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。js的 [[Prototype]] 机制本质上就是行为委托机制。我们可以在js中努力实现类机制,也可以选择更自然的 [[Prototype]] 委托机制。

当你只用对象来设计代码时,不仅可以让语法更加简洁,而且可以让代码结构更加清晰。

对象关联(对象之间互相关联)是一种编码风格,它倡导的是直接创建和关联对象,不把它们抽象成类。对象关联可以记忆[[Prototype]]的行为委托非常自然地实现。

ES6的class语法只是现有 [[Prototype]] (委托!) 机制的一种语法糖。

中卷

本书中,我们这样定义类型”类型是“值”内部的特征,它定义了值的行为,以使其区别于其他值。

js有七中内置类型:

空值(null)

未定义(undefined)

布尔值(boolean)

数字(number)

字符串(string)

对象(object)

符号(symbol , es6新增)

我们可以用 typeof 运算符查看值的类型,它返回的是类型的字符串值。

null 是唯一一个用 tyoeof 检测会返回 "object" 的基本类型值。 //一般用复合条件对null进行检测 var a = null ;  (!a && typeof a === "object"); //true

还有一种情况: typeof function a {/*...*/} === "function" 

这样看来function也是js的一个内置类型。然而查阅规范就知道,它实际上是Object的一个“子类型”。具体来说,函数式可调用对象,因为其具有内置属性 [[call]] ,该属性使其可以被调用。

函数不仅是对象,还可以用有属性,它的length,是其参数数。

数组也是对象的一个“子类型”。

1.3 值和类型

js中变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。

js不做“类型强制”,也就是说,语言引擎不要求变量总是持有与其初始值同类型的值。

undeclared(未声明):表示变量还未声明过。通过 typeof 的安全防范机制(阻止报错)来检查undeclared变量,有时是个不错的办法。

2.1 数组

delete 运算符删除数组单元,数组length不会改变。

“稀疏”数组(sparse array,即含有空白单元或空缺单元的数组)。

2.3.3 较小的数值

误差范围值,机器精度: 2^-52(2.220446049250313e-16);

从ES6开始该值定义在 Number.EPSILON中,可以直接使用,也可以为ES6之前的版本写polyfill:

if (!Number.EPSILON){
   Number.EPSILON = Math.pow(2,-52);  
}

也可用来比较两个数是否相等:

Math.abs() 获取绝对值;

Math.abs(n1-n2)<Number.EPSILON ; 

最大浮点数:Number.MAX_VALUE; //1.798e+308;

整数安全范围:

最大整数:Number.MAX_SAFE_INTEGER; //2^53-1,即9007199254740991

整数检测:typeof num === "number" && num % 1 ==0;

检测一个值是否是安全整数:Number.isSafeinteger();

null和undefined 它们的名称即是类型也是值。

null指空值,undefined指没值。

null指曾赋过值但目前没值,undefined指从未赋值。

永远不要重新定义undefined

void运算符:通过void运算符即可得到该值。总之,如果要将代码中的值设为undefined,就可以使用void。

2.4.3 特殊的数字

NaN 是一个警戒值,用于指出数字类型中的错误,即“执行数学运算没有成功,这是失败后返回的结果。”

NaN 是一个特殊值,是唯一一个非自反(自反,reflexive,即 x===x 不成立)的值。而 NaN != NaN 为true。

利用isNaN()来判断一个值是否是NaN,缺陷不能判断类型,“检查参数是否不是NaN,也不是数字”,ES6提供工具函数 Number.isNaN();

NaN是js中唯一一个不等于自身的值。

isNegZero() , 来判断一个值是否为 -0 。

ES6中新加入了一个工具方法 Object.is() 来判断两个值是否绝对相等。

2.5 值和引用

我们无法决定使用值复制还是引用复制,一切由值得类型来确定。

2.6 小结

js中数组就是通过数字索引的一组任意类型的值。

js中数字包括整数和浮点数。

null类型只有一个值null。

undefined类型也只有一个值undefined。

变量赋值前默认都是undefined。void运算符返回undefined。

数字类型特殊值:isNaN ,-0 ,+Infinity 和 -Infinity。

简单标量基本类型值(字符串和数字等)通过值复制来赋值/传递,而复合值(对象等)通过引用复制来赋值/传递。

js中引用和其它语言中的引用/指针不同,它们不能指向别的变量/引用,只能指向值。

第三章 原生函数 

 常用的原生函数有:

String()

Number()

Boolean()

Array()

Object()

Function()

RegExp()

Date()

Error()

Symbol()--ES6新增

原生函数可以被当做构造函数来用,但是创建出来的是封装了基本类型值的封装对象。

3.1内部属性[[class]]

所有typeof返回值为"object"的对象都包含一个内部属性[[class]] (我们可以看做是一个内部的分类,而不是传统的面向对象意义上的类)

一般通过 Object.prototype.toString.(..) 来查看。 比如: Object.prototype.toString.call([1,2,3])  "[object Array]"

3.4.1 Array(..)

构造函数Array(..)不要求必须带new关键字。不带的时候会自动补上。因此 Array(1,2,3) 和 new Array(1,2,3) 的效果是一样的。

Array 构造函数只带一个数字参数时,该参数会被作为数组的预设长度(length),而非只充当数组一个元素。

我们将包含至少一个“空单元”的数组成为“稀疏数组”。

可以通过 delete 和 将 length 设置更长制造“空单元”。

apply 是一个工具函数,适用于所有函数对象,它会以一种特殊的方式来调用传递给它的函数。

总之,永远不要创建和使用空单元数组。

3.4.2 Object()、Function()、RegExp()

尽量不要 new Object() ,这样只能逐条属性添加。

动态定义函数参数和函数体才会使用构造函数。

建议以常量方式定义正则,RegExp()可以在动态定义正则时候使用。

3.4.3 Date() 和 Error()

除Error外,还有一些特定的错误类型构造函数,ReferenceError()等,程序发生异常时会被自动调用。

3.4.4 Symbol()

ES6新增的一个基本数据类型——符号(Symbol)

符号是具有唯一性的特殊值(并非绝对)

用它,来命名对象属性不容易导致重名。

3.4.5 原生原型

 

 3.5 小结

JS为基本数据类型提供了封装对象,成为原生函数(如:String,Number,Boolean等)。它们作为基本类型值提供了该子类型所特有的方法和属性。

对于简单标量基本类型值,比如“abc”,如果要访问它的 length 属性 或 String.prototype 方法,JS引擎会自动对该值进行封装(即用相应类型的封装对象来包装它)来实现对这些属性和方法的访问。

 

posted @ 2021-05-26 12:33  AZUKI七  阅读(236)  评论(0编辑  收藏  举报