js数据结构

数据结构

所谓数据结构,就是计算机存储和组织数据的方式。说得通俗一点,主要就是指将数据以什么样 的结构存储到计算机里面。在程序里面,最为常见的数据结构,就是数组,这种结构将多个数据 有序的排列在一起,形成了一个组合。除了数组以外,集合,映射等ES6新增加的数据结构

创建数组的方式大致可以分为两种:字面量创建数组和使用构造函数创建数组。

1. 字面量创建数组

2. let arr = [];

3. 构造函数创建数组

let arr = new Array();

1. 先声明再赋值

let arr = [];

arr[0] = 1;

arr[1] = 2;

arr[2] = 3;

注意下标是从o开始的。

2. 声明时直接赋值

let arr = [1,2,3,4,5];

需要注意的是我们可以在数组的任意位置进行赋值,数组的长度会自动改变,空的位置使用

undefined来进行填充

let arr = [];

arr[0] = 1;

arr[4] = 10;

console.log(arr);

什么是集合

在ES6标准制定以前,可选的数据结构类型有限,可以说只有数组这种数据结构。而数组使用的 又是数值型索引,因而经常被用于来模拟队列和栈的行为。但是如果需要使用非数值型索引,就 会用非数组对象创建所需的数据结构,而这就是Set集合与后面一节要介绍的Map映射的早期实 现。

Set集合是一种无重复元素的列表,这是这种数据结构的最大的一个特点。

要创建一个集合,方法很简单,直接使用new就可以创建一个Set对象。如果想要集合在创建时 就包含初始值,那么我们可以传入一个数组进去。

let s1 = new Set();

let s2 = new Set([1,2,3]);

console.log(s1);//Set {}

console.log(s2);//Set { 1, 2, 3 }

4-3-3给集合添加值

使用add()方法可以给一个集合添加值,由于调用add()方法以后返回的又是一个Set对象,所 以我们能连续调用add()方法进行值的添加,这种像链条一样的方法调用方式被称为链式调 用。

let s1 = new Set();

s1.add(1);

console.log(s1);//Set { 1 }

s1.add(2).add(3).add(4);

console.log(s1);

//Set { 1, 2, 3, 4 } 我们还可以直接将一个数组传入add()方法里面

let s1 = new Set();

s1.add([1,2,3]);

console.log(s1);

//Set { [ 1, 2, 3 ] }

let s1 = new Set([1,2,3]);

console.log(s1);//Set { 1, 2, 3 } console.log(s1.size);//3

调用add()方法时传入数组,就是作为Set对象的一个元素

let s1 = new Set();

s1.add([1,2,3]);

console.log(s1);//Set { [ 1, 2, 3 ] } console.log(s1.size);//1

在Set对象中,不能够添加相同的元素,这是很重要的一个特性

let s1 = new Set();

s1.add(1).add(2).add(2).add(3);

console.log(s1);

//Set { 1, 2, 3 }

1. size属性获取元素个数

let s1 = new Set([1,2,3]);

console.log(s1.size);//3

2. 使用hasO方法来查看一个集合中是否包含某一个值

let s1 = new Set([1,2,3]);

console.log(s1.has(1));//true

3. 删除集合值

使用delete删除Set对象里面的某一个元素

let s1 = new Set([1,2,3]);

s1.delete(2);

console.log(s1);//Set { 1, 3 }

//没有的元素也不会报错

s1.delete("2");

console.log(s1);//Set { 1, 3 }

如果要一次性删除所有的元素,可以使用clear方法

let s1 = new Set([1,2,3]);

s1.clear()

console.log(s1);//Set {}

遍历集合

集合也是可以枚举的,我们同样可以使用for-o f来对集合进行遍历,如下:

let s = new Set([1,2,3,4,5]);

for(let i of s){ console.log(i);

}

// 1

// 2

// 3

// 4

// 5


除此之外,我们也可以使用集合里面自带的keys() , values()以及ent ries()方法来对集合 进行遍历。顺便要说一下的是,在集合里面键和值是相同的。

keys()方法遍历集合的键

let s = new Set(["Bill","Lucy","David"]); for(let i of s.keys()){

console.log(i);

}

// Bill

// Lucy

// David

values()方法遍历集合的值

let s = new Set(["Bill","Lucy","David"]); for(let i of s.values()){

console.log(i);

}

// Bill

// Lucy

// David

ent ries()方法同时遍历集合的键与值

let s = new Set(["Bill","Lucy","David"]); for(let i of s.entries()){

console.log(i);

}

// [ 'Bill', 'Bill' ]

// [ 'Lucy', 'Lucy' ]

// [ 'David', 'David' ]

集合转数组

将集合转为数组,最快的方法就是使用前面所讲过的扩展运算符,如下:

let s1 = new Set([1,2,3]);

console.log(s1);//Set { 1, 2, 3 }

let arr = [...s1]; console.log(arr);//[ 1, 2, 3 ]

除此之外,我们还可以使用Arra y对象所提供的from。方法来进行转换

let s1 = new Set([1,2,3]); console.log(s1);//Set { 1, 2, 3 }

let arr = Array.from(s1); console.log(arr);//[ 1, 2, 3 ]

前面我们有提到过,Set对象里面是不能够存放相同的元素的,利用这个特性,我们可以快速的 为数组去重,如下:

let arr = [1,2,2,3,4,3,1,6,7,3,5,7];

let s1 = new Set(arr);

let arr2 = [...s1]; console.log(arr2);//[ 1, 2, 3, 4, 6, 7, 5 ]

什么是内存泄漏?

—个程序里面保留着已经不能在内存中访问的值时,就会发生内存泄露,也就是说占着空间却没 用,造成内存的浪费。

例如:

let arr = [1,2,3]; arr = null;

断开了arr对1, 2, 3的引用,现在1, 2, 3在内存里面已经是垃圾了。内存泄露会逐渐减少全部 可用内存,导致程序和系统的速度变慢甚至崩溃

那么怎样才能清空这些没用的数据呢?例如上例中的1, 2, 3。事实上在JavaScript中采用的是 动态内存管理技术,比如垃圾回收机制,会自动从内存中删除不再被程序需要的东西。而有些编 程语言,例如C++,则是需要程序员手动的管理内存,在某些东西完成任务之后,将其从内存中 删除。

那么,集合的问题就在于即使失去了引用,也不会被垃圾回收,这个时候我们可以使用弱集合来 避免这种状况。创建弱集合使用new运算符和WeakSet()

let weak = new WeakSet();

弱集合无法添加基本数据类型,也就是说无法像集合那样添加简单值进去。

let weak = new WeakSet();

weak.add(1);

//TypeError: Invalid value used in weak set

除了这个限制以外,弱集合和普通集合还有一些细微的区别,例如无法在创建弱集合时传入一个 数组进行初始化。不过弱集合也拥有has() , add() , delete()等方法。

let arr = [1,2,3,4,5];

let weak = new WeakSet(arr);

//TypeError: Invalid value used in weak set

//无法在创建弱集合时传入一个数组进行初始化

还需要注意一点的是,弱集合是对对象的弱引用,所以不能访问对象里面的值列表。这使得弱集 合看上去像是空的,但是并不是空的,证明如下:

let weak = new WeakSet();

let arr = [1,2,3];

weak.add(arr);

console.log(weak);//WeakSet {}

console.log(weak.has(arr));//true

console.log(s1.has("1"));//false

使用new关键字与Map()构造函数,就可以创建一个空的m ap对象。如果要向Map映射中添加 新的元素,可以调用set()方法并分别传入键名和对应值作为两个参数。如果要从集合中获取 信息,可以调用get()方法。

let m = new Map();

m.set("name","xiejie");

m.set("age",18);

console.log(m);

//Map { 'name' => 'xiejie', 'age' => 18 }

console.log(m.get("name"));

//xiejie

在对象中,无法用对象作为对象属性的键名。但是在Map映射中,却可以这样做,可以这么说, 在Map映射里面可以使用任意数据类型来作为键。

let m = new Map();

m.set({},"xiejie"); m.set([1,2,3],18);

m.set(3581,18);

console.log(m);

//Map { {} => 'xiejie', [ 1, 2, 3 ] => 18, 3581 => 18 }传入数组来初始化Map映射 可以向Map构造函数传入一个数组来初始化Map映射,这一点同样与Set集合相似。数组中的每 个元素都是一个子数组,子数组中包含一个键值对的键名与值两个元素。因此,整个Map映射中 包含的全是这样的两个元素的二维数组

let arr = [["name","xiejie"],["age",18]];

let m = new Map(arr);

console.log(m);

//Map { 'name' => 'xiejie', 'age' => 18 }

Map结构转为数组结构,比较快速的方法还是使用前面介绍过的扩展运算符...。

let arr = [["name","xiejie"],["age",18]];

let m = new Map(arr);

console.log([...m.keys()]);//[ 'name', 'age' ]

console.log([...m.values()]);//[ 'xiejie', 18 ]

console.log([...m.entries()]);//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ]

console.log([...m]);//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ] 或者使用Array对象的from。方法

let arr = [["name","xiejie"],["age",18]];

let m = new Map(arr);

console.log(Array.from(m));

//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ]

函数

函数,是可以通过名称来引用,并且就像自包含了一个微型程序的代码块。利用函数,我们可以 实现对代码的复用,降低代码的重复,并且让代码更加容易阅读。在JavaScript中,函数显得尤 为的重要。因为函数在JavaScript中是一等公民,可以像参数一样传入和返回。所以说函数是 JavaScript中的一个重点,同时也是一个难点。

为什么需要函数

首先我们来看一下为什么需要函数。函数最大的好处就是可以对代码实现复用。相同的功能不用 再次书写,而是只用书写一次就够了。这其实就是编程里面所谓的DRY原则

所谓DRY原则,全称为Don‘t repeat yourself,翻译成中文就是不要重复你自己。什么意思 呢?也就是说一个程序的每个部分只被编写一次,这样做可以避免重复,不需要保持多个代 码段的更新和重复。

并且,我们可以把函数看作是一个暗箱,不需要知道函数的内部是怎么实现的,只需要知道函数 的功能,参数以及返回值即可。

声明函数的方式
在JavaScript中,声明函数的方式有多种,这里我们先来介绍这3种声明函数的方式:字面量声明 函数,函数表达式声明函数以及使用构造器方式来声明一个函数。

1. 字面量声明函数

这种方式是用得最为广泛的一种方式,使用关键字function来创建一个函数,具体的语法如 下:

function函数名(形式参数){

//函数体

}

函数名:就是我们调用函数时需要书写的标识符

形式参数:简称形参,是调用函数时需要接收的参数

实际参数:简称实参,是调用函数时实际传递过去的参数

function test(name){

console.log("Hello,"+name);

}

test("xiejie");//Hello,xiejie

2. 函数表达式声明函数

第二种方式是使用函数表达式来进行声明,具体的语法如下:

let 变量 =function(){

//函数体

}

函数表达式示例:

let test = function(name){

console.log("Hello,"+name);

} test("xiejie");//Hello,xiejie

我们也可以将一个带有名字的函数赋值给一个变量。这样的声明方式被称之为命名式函数 表达式

示例如下:

let test = function saySth(name){

console.log("Hello,"+name);

}

test("xiejie");//Hello,xiejie

注意:虽然这种方式的函数表达式拥有函数名,但是调用的时候还是必须要通过变量名来调 用,而不能够使用函数名来进行调用。

函数的调用

函数的调用在前面介绍函数声明的时候也已经见到过了,就是写上函数名或者变量名,后面加上 —对大括号即可。需要注意的是,一般来讲函数表示的是一个动作,所以在给函数命名的时候, —般都是以动词居多。

还一个地方需要注意,那就是如果要调用函数,那么就必须要有括号。这个括号要么在函数名后 面,要么在变量名后面,这样才能够将调用函数后的执行结果返回。如果缺少了括号,那就只是 引用函数本身。

let test = function(){ console.log("Hello");

}

let i = test;//没有调用函数,而是将test函数赋值给了i i();//Hello

函数的返回值

函数的返回值的关键字为ret urn。代表要从函数体内部返回给外部的值,示例如下:

let test = function(){ return "Hello";

}

let i = test();

console.log(i);//Hello

即使不写return ,函数本身也会有返回值undefined 例如:

let test = function(){

console.log("Hello");

}

let i = test();//Hello

console.log(i);//undefined

需要注意的是,retu rn后面的代码是不会执行的,因为在函数里面一旦执行到ret urn ,整个 函数的执行就结束了。

let test = function(){ return 1; console.log("Hello");

}

let i = test();

console.log(i);//1

retu rn关键字只能返回一个值,如果想要返回多个值,可以考虑返回一个数组。

函数的参数

函数的参数可以分为两种,一种是实际参数,另外一种是形式参数。这个我们在前面已经介绍过 了。接下来我们来详细看一下形式参数。形式参数简称形参,它就是一种变量,但是这种变量只 能被函数体内的语句使用,并在函数调用时被赋值。JavaScript中的形参的声明是不需要添加关 键字的,如果加上关键字反而会报错

示例:

function test(let i){ console.log(i);

}

test(5);

//SyntaxError: Unexpected identifier

JavaScript里面关于函数的形参,有以下几个注意点: 1•参数名可以重复,同名的参数取最后一个参数值

function test(x,x){

console.log(x);

}

test(3,5);//5

2•即使函数声明了参数,调用时也可以不传递参数值

function test(x){ console.log(x);

}

test();//undefined

3•调用函数时可以传递若干个参数值给函数,而不用管函数声明时有几个参数

function test(x){

console.log(x);//1

}

test(1,2,3);

那么,这究竟是怎么实现的呢?为什么实参和形参可以不用一一对应呢?

实际上,当一个函数要被执行的时候,系统会在执行函数体代码前做一些初始化工作,其中之一 就是为函数创建一个arguments的伪数组对象。这个伪数组对象将包含调用函数时传递的所有的 实际参数。因此,arguments的主要用途是就是用于保存传入到函数的实际参数的。

下面的例子演示了通过arguments伪数组对象来访问到所有传入到函数的实参

function test(x){

for( let i=0;i<arguments.length;i++){

console.log(arguments[i]);

}

}

test(1,2,3);

// 1

// 2

// 3

所谓伪数组对象,就是长得像数组的对象而已,但是并不是真的数组,我们可以证明这一点

function test(x){

ar guments .push( 100); //针对伪数组对象使用数组的方法

}

test(1,2,3);

//TypeError: arguments.push is not a function

不定参数

不定参数是从ES6开始新添加的功能,在最后一个形式参数前面添加3个点,会将所有的实参放 入到一个数组里面,示例如下:

function test(a,...b){

console.log(a);//1

console.log(b);//[2,3]

test(1,2,3);

这里的不定参数就是一个真正的数组,可以使用数组的相关方法

function test(a,...b){ console.log(a);//1 console.log(b);//[2,3] b.push(100);

console.log(b);//[ 2, 3, 100 ]

} test(1,2,3);

还有一点需要注意的是,不定参数都是放在形式参数的最后面,如果不是放在最后,则会报错。

function test(...a,b){ console.log(a); console.log(b);

} test(1,2,3);

//SyntaxError: Rest parameter must be last formal parameter

默认参数

从ES6开始,书写函数的时候可以给函数的形式参数一个默认值。这样如果在调用函数时没有传 递相应的实际参数,就使用默认值。如果传递了相应的实际参数,则使用传过去的参数。

function test(name = "world"){ console.log("Hello,"+name);

} test("xiejie");//Hello,xiejie test();//Hello,world

如果参数是一个数组,要为这个数组设置默认值的话,写法稍微有些不同,如下:

let fn = function([a = 1,b = 2] = []){ console.log(a,b);

}

fn(); // 1 2

fn([3,4]); // 3 4

包括后面我们要介绍的对象,也是可以设定默认值的,但是写法和上面类似,如下:

let fn = function({name = 'xiejie',age = 18} = {}){ console.log(name,age);

}

fn(); // xiejie 18

fn({name:"song",age:20}); // song 20

 

 

 

原文地址:https://www.cnblogs.com/akangwx0624/p/11149258.html