简介

基本上所有的程序员都应用过javascript,咱们在web中应用javascript,咱们在服务器端应用nodejs,js给大家的第一映像就是简略,然而可能并不是所有人都零碎的理解过js中的内置对象和数据结构。

明天,一起来看看吧。

根底类型

js是一种弱类型的动静语言,尽管是弱类型的,然而js自身定义了很多种数据类型。

js中有7种根底类型:别离是undefined,Boolean,Number,String,BigInt,Symbol和null。

undefined

undefined会主动赋值给刚刚申明的变量。举个例子:

var x; //create a variable but assign it no valueconsole.log("x's value is", x) //logs "x's value is undefined"

Boolean和Boolean对象

Boolean的值就是true 或者 false。

除了根底类型的Boolean值外,还有一个Boolean对象,用来封装boolean值。

如果是用new Boolean来结构Boolean对象的话,上面的例子中Boolean的初始值都是false:

var bNoParam = new Boolean();var bZero = new Boolean(0);var bNull = new Boolean(null);var bEmptyString = new Boolean('');var bfalse = new Boolean(false);

上面boolean对象的初始值都是true:

var btrue = new Boolean(true);var btrueString = new Boolean('true');var bfalseString = new Boolean('false');var bSuLin = new Boolean('Su Lin');var bArrayProto = new Boolean([]);var bObjProto = new Boolean({});

留神,咱们不要应用Boolean对象来进行if条件的判断,任何Boolean对象,即便是初始值是false的Boolean对象,if条件判断,都是true:

var x = new Boolean(false);if (x) {  // this code is executed}var x = false;if (x) {  // this code is not executed}

如果非要应用if条件判断,咱们能够应用Boolean函数或者!!如下所示:

var x = Boolean(expression);     // use this...var x = !!(expression);          // ...or thisvar x = new Boolean(expression); // don't use this!

Number和BigInt

Number和BigInt是JS中的两个数字类型,其中Number示意的双精度64位二进制格局,其范畴是-(253 − 1) and 253 − 1.

除此之外,Number还有三个值:+Infinity, -Infinity, 和 NaN。

后面两个示意的是正负最大值。NaN示意的是 Not-A-Number。

咱们能够通过isNaN来判断是否是一个Number:

function sanitise(x) {  if (isNaN(x)) {    return NaN;  }  return x;}console.log(sanitise('1'));// expected output: "1"console.log(sanitise('NotANumber'));// expected output: NaN

BigInt示意任意精度的整数,应用BigInt能够进行超出Number精度整数的运算。

咱们能够通过在整数前面加上n来示意BigInt。

> const x = 2n ** 53n;9007199254740992n> const y = x + 1n; 9007199254740993n
留神,和Boolean一样,Number和BitInt也有wrapper对象类型。

看下Number的wrapper:

Number('123')  // returns the number 123Number('123') === 123  // trueNumber("unicorn")  // NaNNumber(undefined)  // NaN

看下BitInt的wrapper类型:

const theBiggestInt = 9007199254740991nconst alsoHuge = BigInt(9007199254740991)// ↪ 9007199254740991nconst hugeString = BigInt("9007199254740991")// ↪ 9007199254740991nconst hugeHex = BigInt("0x1fffffffffffff")// ↪ 9007199254740991nconst hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111")// ↪ 9007199254740991n

String

js中的String是不可变的,同样的String根底类型也有和它对应的String wrapper对象。

String根底类型和不应用new的String函数是统一的:

const string1 = "A string primitive";const string2 = String('A string primitive');

下面两个String是统一的。然而如果应用new来结构String对象,那么两者是不一样的:

let s_prim = 'foo'let s_obj = new String(s_prim)console.log(typeof s_prim) // Logs "string"console.log(typeof s_obj)  // Logs "object"let s1 = '2 + 2'              // creates a string primitivelet s2 = new String('2 + 2')  // creates a String objectconsole.log(eval(s1))         // returns the number 4console.log(eval(s2))         // returns the string "2 + 2"

咱们能够通过String对象的valueOf()办法,取得其String根底类型。

Symbol

Symbol是一个惟一的不可变的根底类型,个别用在对象的key中。

// Here are two symbols with the same description:let Sym1 = Symbol("Sym")let Sym2 = Symbol("Sym")  console.log(Sym1 === Sym2) // returns "false"

Symbol是不反对new操作的:

let sym = new Symbol()  // TypeError

如果你真的想创立Symbol对象,则能够应用Object():

let sym = Symbol('foo')typeof sym      // "symbol" let symObj = Object(sym)typeof symObj   // "object"

null

null示意援用的是有效的Object对象或者地址。

尽管null能够看做是primitive,然而null其实是一个Object,所有的对象都来自null:

typeof null === 'object' // true

Object

Object是js中的一种数据类型,简直所有的对象都继承自Object,它存储的是key-value模式的数据,咱们能够通过应用Ojbect()办法或者new Object()或者Object字面量的形式来创立Object。

let o = {}let o = {a: 'foo', b: 42, c: {}}let a = 'foo', b = 42, c = {}let o = {a: a, b: b, c: c}
留神应用Object()或者new Object()是一样的成果,都会失去一个Object对象。

在ES2015之后,咱们还能够应用动静的对象属性:

let param = 'size'let config = {  [param]: 12,  ['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4}console.log(config) // {size: 12, mobileSize: 4}

Function

Function也是一个Object,JS中的所有函数都是Function对象。

(function(){}).constructor === Function

那么通过Function构造函数和function函数定义创立进去的函数有什么区别呢?

应用new Function创立的函数,其作用域范畴是global,咱们看一下具体的例子:

var x = 10;function createFunction1() {    var x = 20;    return new Function('return x;'); // this |x| refers global |x|}function createFunction2() {    var x = 20;    function f() {        return x; // this |x| refers local |x| above    }    return f;}var f1 = createFunction1();console.log(f1());          // 10var f2 = createFunction2();console.log(f2());          // 20

Date

Date是js中用来操作工夫的Object。咱们看下Date的罕用例子:

let today = new Date()let birthday = new Date('December 17, 1995 03:24:00')let birthday = new Date('1995-12-17T03:24:00')let birthday = new Date(1995, 11, 17)            // the month is 0-indexedlet birthday = new Date(1995, 11, 17, 3, 24, 0)let birthday = new Date(628021800000)            // passing epoch timestamplet [month, date, year]    = ( new Date() ).toLocaleDateString().split("/")let [hour, minute, second] = ( new Date() ).toLocaleTimeString().slice(0,7).split(":")

Array

JS内置了很多种不同类型的Array,最罕用的就是Array字面量和Array Object。

咱们看下怎么创立一个Array:

let fruits = ['Apple', 'Banana'];console.log(fruits.length); // 2console.log(fruits[0]);     // "Apple"let fruits = new Array('Apple', 'Banana');console.log(fruits.length); // 2console.log(fruits[0]);     // "Apple"

遍历Array:

let fruits = ['Apple', 'Banana']fruits.forEach(function(item, index, array) {  console.log(item, index)})// Apple 0// Banana 1

增加Item到Array:

let newLength = fruits.push('Orange')// ["Apple", "Banana", "Orange"]

从最初删除item:

let last = fruits.pop() // remove Orange (from the end)// ["Apple", "Banana"]

从后面删除item:

let first = fruits.shift() // remove Apple from the front// ["Banana"]

从后面增加item:

let newLength = fruits.unshift('Strawberry') // add to the front// ["Strawberry", "Banana"]

删除某个index的item:

let removedItem = fruits.splice(pos, 1) // this is how to remove an item// ["Strawberry", "Mango"]

删除多个item:

let vegetables = ['Cabbage', 'Turnip', 'Radish', 'Carrot']console.log(vegetables)// ["Cabbage", "Turnip", "Radish", "Carrot"]let pos = 1let n = 2let removedItems = vegetables.splice(pos, n)// this is how to remove items, n defines the number of items to be removed,// starting at the index position specified by pos and progressing toward the end of array.console.log(vegetables)// ["Cabbage", "Carrot"] (the original array is changed)console.log(removedItems)// ["Turnip", "Radish"]

拷贝array:

let shallowCopy = fruits.slice() // this is how to make a copy// ["Strawberry", "Mango"]

除了Array之外,JS还内置了特定类型的Array:

  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array
  • BigInt64Array
  • BigUint64Array

这些特定类型的Array中只能存储特定类型的值。

Keyed collections

除了数组之外,JS中还有key-value的汇合,比方:Map,Set,WeakMap和WeakSet。

对Map来说,咱们能够通过应用set,get,has,delete等犯法来对Map进行操作:

let contacts = new Map()contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})contacts.has('Jessie') // truecontacts.get('Hilary') // undefinedcontacts.set('Hilary', {phone: "617-555-4321", address: "321 S 2nd St"})contacts.get('Jessie') // {phone: "213-555-1234", address: "123 N 1st Ave"}contacts.delete('Raymond') // falsecontacts.delete('Jessie') // trueconsole.log(contacts.size) // 1

遍历Map:

let myMap = new Map()myMap.set(0, 'zero')myMap.set(1, 'one')for (let [key, value] of myMap) {  console.log(key + ' = ' + value)}// 0 = zero// 1 = onefor (let key of myMap.keys()) {  console.log(key)}// 0// 1for (let value of myMap.values()) {  console.log(value)}// zero// onefor (let [key, value] of myMap.entries()) {  console.log(key + ' = ' + value)}// 0 = zero// 1 = one

应用forEach来遍历map:

myMap.forEach(function(value, key) {  console.log(key + ' = ' + value)})// 0 = zero// 1 = one

Set中存储的是惟一的对象。

咱们看下Set的操作:

let mySet = new Set()mySet.add(1)           // Set [ 1 ]mySet.add(5)           // Set [ 1, 5 ]mySet.has(1)              // truemySet.delete(1)    // removes 1 from the set

set的遍历:

// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2} for (let item of mySet) console.log(item)

WeakMap,WeakSet和Map于Set的区别在于,WeakMap的key只能是Object对象,不能是根本类型。

为什么会有WeakMap呢?

对于JS中的Map来说,通常须要保护两个数组,第一个数组中存储key,第二个数组中存储value。每次增加和删除item的时候,都须要同时操作两个数组。

这种实现有两个毛病,第一个毛病是每次查找的时候都须要遍历key的数组,而后找到对应的index,再通过index来从第二个数组中查找value。

第二个毛病就是key和value是强绑定的,即便key不再被应用了,也不会被垃圾回收。

所以引入了WeakMap的概念,在WeakMap中,key和value没有这样的强绑定关系,key如果不再被应用的话,能够被垃圾回收器回收。

因为援用关系是weak的,所以weakMap不反对key的遍历,如果你想遍历key的话,请应用Map。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/js-built-in-objects-structures/

本文起源:flydean的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!