简介
基本上所有的程序员都应用过 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 value
console.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 this
var 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 123
Number('123') === 123 // true
Number("unicorn") // NaN
Number(undefined) // NaN
看下 BitInt 的 wrapper 类型:
const theBiggestInt = 9007199254740991n
const alsoHuge = BigInt(9007199254740991)
// ↪ 9007199254740991n
const hugeString = BigInt("9007199254740991")
// ↪ 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff")
// ↪ 9007199254740991n
const 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 primitive
let s2 = new String('2 + 2') // creates a String object
console.log(eval(s1)) // returns the number 4
console.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()); // 10
var 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-indexed
let birthday = new Date(1995, 11, 17, 3, 24, 0)
let birthday = new Date(628021800000) // passing epoch timestamp
let [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); // 2
console.log(fruits[0]); // "Apple"
let fruits = new Array('Apple', 'Banana');
console.log(fruits.length); // 2
console.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 = 1
let n = 2
let 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') // true
contacts.get('Hilary') // undefined
contacts.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') // false
contacts.delete('Jessie') // true
console.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 = one
for (let key of myMap.keys()) {console.log(key)
}
// 0
// 1
for (let value of myMap.values()) {console.log(value)
}
// zero
// one
for (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) // true
mySet.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 的博客
欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!