夕顔 | 就算是痴人说梦那又怎样

HTML

元素分类

  • 行内元素(7):a b(加粗) span img input selcet strong
  • 块级元素(9):div ul ol li dl dt dd h1 p
  • 空元素(6):br hr(直线) img input link meta
  • 加载顺序(link 同时加载 @import 等页面加载完再加载)
  • 分类不同(link 是 XHTML 标签,除 CSS 还可加载其他文件 @import 由 css 提供,只能加载 css)
  • 兼容性(@import IE5 以上)

HTML5 新特性

HTML5新特性浅谈

  • 语义新特性(结构元素:article、footer、header、nav、section;表单控件:calendar、date、time、email、url、search;)
  • 本地存储特性
  • 设备访问特性
  • 连接特性(WebSocket)
  • 网络多媒体特性(Audio、Video)
  • 三维、图形及特效特性(SVG、Canvas、WebGL)
  • 性能与集成特性(WebWorkers)

HTML5 离线缓存

Manifest文件组成:

  • CACHE(必须 需要缓存的文件)
  • FALLBACK(可选 后备页面)

如何更新缓存?

给manifest添加或删除文件,都可更新缓存。

1
window.applicationCache.update();

CSS

盒模型

盒模型

CSS选择器

  1. id选择器
  2. 类选择器
  3. 标签选择器
  4. 相邻选择器
  5. 子选择器
  6. 后代选择器
  7. 通配符选择器
  8. 属性选择器
  9. 伪类选择器

选择器权重

内联样式表(标签内部)> 嵌入样式表(当前文件中)> 外部样式表(外部文件中)。
!important > id > class > tag
!important 比 内联优先级高

CSS3 新特性

深入了解 CSS3 新特性

CSS3 选择器

1
2
3
4
5
6
7
8
9
10
11
p:first-of-type	选择属于其父元素的首个 <p> 元素的每个 <p> 元素。
p:last-of-type 选择属于其父元素的最后 <p> 元素的每个 <p> 元素。
p:only-of-type 选择属于其父元素唯一的 <p> 元素的每个 <p> 元素。
p:only-child 选择属于其父元素的唯一子元素的每个 <p> 元素。
p:nth-child(2) 选择属于其父元素的第二个子元素的每个 <p> 元素。

::after 在元素之前添加内容,也可以用来做清除浮动。
::before 在元素之后添加内容
:enabled
:disabled 控制表单控件的禁用状态。
:checked 单选框或复选框被选中。

DIV 居中

1
2
3
4
div{
width:200px;
margin:0 auto;
}
1
2
3
4
5
6
7
8
9
10
div {
position: absolute;
width: 300px;
height: 300px;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
1
2
3
4
5
6
7
8
div {
position: relative; /* 相对定位或绝对定位均可 */
width:500px;
height:300px;
top: 50%;
left: 50%;
margin: -150px 0 0 -250px; /* 外边距为自身宽高的一半 */
}
1
2
3
4
5
6
7
8
div {
position: absolute; /* 相对定位或绝对定位均可 */
width:500px;
height:300px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
1
2
3
4
5
6
7
8
9
10
.container {
display: flex;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */

}
.container div {
width: 100px;
height: 100px;
}

display有哪些值

CSS display 属性

table凉了,自行点击上面网站查看👆

inline、inline-block、block的区别

inline

  1. 不能设置高宽

  2. margin只对横向生效,纵向无效

  3. 关于padding比较迷,下面来细细说明一下

    code

    给两个inline元素分别设置了100px的padding,我以为会出现上下padding不生效,左右padding生效的效果,而结果比较意外。

    preview

    inline element 與 padding 的二三事

    读完这篇文章你会发现,其实对于inline元素来说,padding四个方向的值都是生效的,只是他不会向块级元素一样影响其他元素的布局,而是产生一种堆叠效果。

    inline padding

    Inline-block:

    1. 可以设置高宽
    2. margin和padding四个方向均生效
    3. 默认不换行

弹性盒模型

Flex 布局教程:语法篇

Flex 布局教程:实例篇

常见布局

多列等高布局

padding补偿法

1
2
3
在需要等高布局等每个元素都设置 很大的正的padding-bottom, 很大的负的margin-bottom;  
padding-bottom: 2000px;
margin-bottom: -2000px;

BFC(块级格式化上下文)

CSS之BFC详解

BFC应用场景

在讲解BFC之前我们首先看一下BFC有哪些应用场景

  1. 垂直方向上margin叠加问题
  2. overflow:hidden 清除浮动

BFC特性

  1. 同一个BFC的两个相邻Box的margin会发生叠加
  2. 计算BFC的高度时,浮动元素也参与计算

BFC触发条件

  1. float 除了none以外的值
  2. overflow 除了visible 以外的值(hidden,auto,scroll )
  3. display (table-cell,table-caption,inline-block, flex, inline-flex)
  4. position值为(absolute,fixed)
  5. fieldset元素

JavaScript

基本类型

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Symbol

内置对象

  • 数据封装类对象:Object Array Boolean Number String
  • 其他对象:Function Arguments Math Date RegExp Error

如何判断一个数据的类型

判断JS数据类型的4种方法

typeof

1
2
3
4
5
6
7
8
9
10
typeof '' // string 有效
typeof 1 // number 有效
typeof Symbol() // symbol 有效
typeof true // boolean 有效
typeof undefined // undefined 有效
typeof new Function() // function 有效
typeof null // object 无效
typeof [] // object 无效
typeof Date() // object 无效
typeof RegExp() // object 无效

instanceof

原型链

instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

constructor

1
2
3
4
5
6
7
8
9
''.constructor == String // true
new Number(1).constructor == Number // true
true.constructor == Boolean // true
new Function().constructor == Function // true
new Date().constructor == Date // true
new Error().constructor == Error // true
[].constructor == Array // true
document.constructor == HTMLDocment // true
window.constructor == Window // true

toString

1
2
3
4
5
6
7
8
9
10
11
12
13
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]

原型链

三日不见,又把原型链还给爸爸了(现在简直是锥心般疯狂焦虑)。幸好我有先见之名,在之前好不容易理清楚后整理了一段录音。现在我们用流程图来展现一下。

每个函数创建后都会有prototype的属性,而对象有[[prototype]]的属性

原型链继承遵循如下一条规则:

1
instance.constructor.prototype = instance.__proto__

而查找属性和方法是沿着实例的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

```javascript
function Parent() {
this.property = true
}
Parent.prototype.getParentValue = function () {
return this.property
}
function Child() {
this.Childproperty = false
}
Child.prototype = new Parent()
Child.prototype.getChildValue = function () {
return this.Childproperty
}
var instance = new Child()
console.log(instance.getParentValue())

所以上述instance.getParentValue()的查找顺序如下:

1
2
3
instance.__proto__ --> Child.prototype --> (new Parent()).__proto__ --> Parent.prototype

// __proto__也可写为[[prototype]] 隐式原型 prototype 显示原型

理清楚了原型链关系,我们来看一道经典原型链的题型:

JS中的原型和原型链(面试中奖率120%)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function Base(name){
this.sex = 0;
this.name = name || 'base';
this.hello = function(){
console.log("hello " + name);
};
}
Base.prototype.say = function(){
console.log('name:'+this.name);
};
function Extend(name,num){
Base.call(this,name);
this.num = num || 0;
}

Extend.prototype = new Base();
// 前面由于将prototype变为了new Base()所以构造方法默认是Base的
// 这里需要手动替换回来
Extend.prototype.constructor = Extend;

var one = new Extend('one',2);

// 因为这道题比较麻烦所以我另起一行说明
// 首先我们需要搞清楚Extend是谁的实例
// 上面的代码我们可以理解成 const Extend = new Function()
// 所以Extend其实是Function的实例
console.log(Extend.__proto__);// --> Function.prototype --> ƒ () { [native code] }

// instanceof 主要用法是判断Extend.prototype是否存在于one的原型链
console.log(one instanceof Extend); // true
console.log(one instanceof Base); // true


console.log(one.constructor === Extend);// true
console.log(one.__proto__ === Extend.prototype);// true


console.log(one.name);// 'one'
console.log(one.sex);// 0
console.log(one.num);// 2
one.say();// name:one
one.hello();// hello one

做完题后的感想是以后若是我有机会面试其他人,一定要用这道题爆锤。

梳理思路的途中,看到一个比较诡异的题:

1
2
3
4
5
Function instanceof Object // true 
Object instanceof Function // true
Function instanceof Function //true
Object instanceof Object // true
Number instanceof Number //false

要搞清楚这道题,我们首先需要搞清楚Function和Object的关系。

Javascript中Function与Object的关系

看完可能还是比较晕,我们来玄学解题,先来看下比较常规的原型链

Object和Function

1
2
3
4
5
6
7
8
9
10
11
12
// 这个就很好解释
Function instanceof Object // true

// 因为Objct和Function都是构造函数,所以均为Function的实例,这一点也是比较容易理解的
Object instanceof Function // true
Function instanceof Function //true

// Object -->(instanceof) Function -->(instanceof) Object
Object instanceof Object // true


Number instanceof Number //false

创建对象的方式

JavaScript创建对象的七种方式

工厂模式

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name, job) {
const o = new Object()
o.name = name
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}

const person = createPerson('Vickie', 'student')

构造函数模式

1
2
3
4
5
6
7
8
9
function Person(name, job) {
this.name = name
this.job = job
this.sayName = function() {
console.log(this.name)
}
}

const person = new Person('Vickie', 'Student')

new 的过程发生了什么

  1. 创建一个新对象
  2. 将构造函数的作用域赋值给新对象(这个对象会执行[[prototype]]链接)
  3. 执行构造函数中的代码
  4. 返回对象

原型模式

1
2
3
4
5
6
7
8
function Person() {}
Person.prototype.name = 'Vickie'
Person.prototype.job = 'student'
Person.prototype.sayName = function() {
console.log(this.name)
}

const person = new Person()

另一种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person() {}
Person.prototype = {
name: 'Vickie',
job: 'student',
sayName: function() {
console.log(this.name)
}
}

// 但是由于constructor在 prototype上 重写字面量覆盖掉了 我们需要重写
// 并且constructor是不可枚举的
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person
})

这里补充一下defineProperty的几个属性:

  • configurable 可配置
  • enumerable 可枚举
  • value
  • writable
  • get
  • set

这里插播一篇vue.js关于Object.defineProperty的利用原理

组合模式

1
2
3
4
5
6
7
function Person(name) {
this.name = name
this.friends = [‘Shelby’, ‘Court’]
}
Person.prototype.sayName = function() {
console.log(this.name)
}

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, job) {
// 属性
this.name = name
this.job = job

// 方法
if(typeof this.sayName !== ‘function’) {
Person.prototype.sayName = function() {
console.log(this.name)
}
}
}

寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
function Person(name, job) {
var o = new Object()
o.name = name
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}
var person1 = new Person(‘Jiang’, ‘student’)

构造函数中重写了返回值,这个乍一看除了new和工厂没什么区别。

那么,这个new有毛用呢?

“寄生器构造函数”可以在构造函数不适应的情况使用,比如要创建一个数组类型,像题主给的代码一样(因为构造函数只能创建对象类型)。为了让人一看就知道是在构造一个新的对象类型的实例,所以虽然它写的和工厂模式一样,但是创建时用了new,因此使得实现的过程不一样,和楼上的回答一样(但是实现过程不重要)。 具体作用,比如创建具有额外方法的已有类型(如数组,Date类型等),但是又不污染原有的类型。 所以就算没有new也一样,只不过加上new让人清楚这是新对象类型的实例,也是“寄生器构造函数”里有个“构造函数”的原因。

那么就是没毛用咯。

稳妥构造函数模式

1
2
3
4
5
6
7
8
9
10
11
function Person(name, job) {
const o = new Object()
o.name = name
o.job = job
o.sayName = function() {
console.log(name)
}
return o
}
var person1 = Person(‘Jiang’, ‘student’)
person1.sayName()

闭包

闭包是指有权访问另外一个函数作用域中的变量的函数。

应用场景:

  1. 结合Symbol实现私有属性
1
2
3
4
5
6
7
8
9
10
11
12
const People = (function() {
const name = Symbol('name')
class People {
constructor(n) {
this[name] = n
}
sayName() {
console.log(this[name])
}
}
return People
})()
  1. 循环绑定事件时利用闭包保存变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
const nodes = document.getElementByTagName('div')
const len = nodes.length
for(let i = 0; i < len; i++) {
(function(i){
nodes[i].onclick = function() {
alert(i)
}
})(i)
}
</script>
</body>
</html>

理解闭包之前,我们需要先理解JavaScript的作用域。

彻底理解js的执行上下文,以及变量对象

彻底理解js的作用域链

如何确定js里的this

总结一下上面几篇文章的内容:

当函数被创建的同时会产生一个执行上下文(对象)

js执行的时候会维持一个执行上下文栈,随着函数的执行,执行上下文(对象)进栈出栈。

这个对象包括几个属性,其中有 变量对象、作用域链

变量对象包括:

  1. 建立arguments对象
  2. 找到这个将要执行的函数内的所有函数声明
  3. 找到这个将要执行的函数内的所有变量声明

作用域链里,大概是这样的(看图体会)

scope chain

这里的0 1,就分别代表爸爸的活动对象,爸爸的爸爸的活动对象。

当搜索引擎解析一个变量的时候,会先在当前的变量对象里查找,没有的话就会沿着作用域链查找。

ES6

ECMAScript6入门 Summary

挑几个常用的总结一下,其他的看阮老师的文章。

箭头函数

关于箭头函数的几个注意点:

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

Web 综合

常见的浏览器内核

  • Trident: IE
  • Gecko: FireFox
  • Presto: Opera
  • Webkit: Safari, Chrome

介绍一下你对浏览器内核的理解?

从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理

进程和线程的区别

  • 进程是 cpu 资源分配的最小单位
  • 线程是 cpu 调度的最小单位

浏览器是多进程的

浏览器

浏览器有以下几个进程:

  1. Browser 进程(主程)
  • 负责浏览器界面显示,与用户交互。如前进,后退等
  • 负责各个页面的管理,创建和销毁其他进程
  • 将 Renderer 进程得到的内存中的 Bitmap,绘制到用户界面上
  • 网络资源的管理,下载等
  1. 第三方插件进程
  2. GPU 进程(3D 绘制)
  3. 浏览器渲染进程 = 浏览器内核(每个 Tab 一个进程,多个空白页自动合并成一个进程。用于页面渲染、脚本运行、事件处理等)

敲黑板,划重点,浏览器的渲染进程是多线程的。那么我们来看看它包括哪些线程。

  1. GUI 渲染线程(Graphical User Interface)
  • 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。
  • 注意,GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
  1. JS 引擎线程
  • 也称为 JS 内核,负责处理 Javascript 脚本程序。
  1. 事件触发线程
  2. 定时触发器线程
  3. 异步 http 请求线程

从打开浏览器到页面渲染的过程

从输入一个 url 后到页面加载完成都发生了什么

从输入 url 到页面加载发生了什么 –Jason 哥

渲染过程

DNS查询过程

  1. 用户输入 URL
  2. 判断是否命中强缓存,若命中,直接返回缓存资源。
  3. 开始 DNS 解析,详细过程参考从输入一个 url 后到页面加载完成都发生了什么
  4. 建立 TCP 连接(三次握手)
  5. TCP 建立完成后发送 HTTP 请求
  6. 服务器接受请求并解析,如果头部有缓存相关信息如 if-none-match 与 if-modified-since,则验证缓存是否有效,若有效则返回 304
  7. 服务器将响应报文通过 TCP 连接发送回浏览器
  8. 浏览器接收响应,并关闭 TCP 连接(四次挥手)
  9. 浏览器检查状态码并进行对应操作
  10. Browser 进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过 RendererHost 接口传递给 Render 进程
  11. Renderer 进程的 Renderer 接口收到消息,简单解释后,交给渲染线程,然后开始渲染
  12. 解析 html 建立 dom 树
  13. 解析 css 构建 render 树(将 CSS 代码解析成树形的数据结构,然后结合 DOM 合并成 render 树)
  14. 布局 render 树(Layout/reflow),负责各元素尺寸、位置的计算
  15. 绘制 render 树(paint),绘制页面像素信息
  16. 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成(composite),显示在屏幕上。

TCP 三次握手和四次挥手

图解 TCP 三次握手与四次分手

TCP 连接的建立和释放

首先我们来看看 TCP 的报文里都有哪些字段。

TCP 报文

下面我们来分别解释一下在连接中我们使用到的字段分别代表什么意思:

  • SYN: 同步序号(synchronous),用来发起一个连接。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段,若对方同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1。
  • ACK: 确认字符(acknowledgement),仅当 ACK=1 时确认号字段才有效,当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有的传送报文段都必须把 ACK 置 1。

三次握手

三次握手

  • 第一次握手:客户端的 TCP 进程首先创建传输控制模块 TCB,然后向服务端发出连接请求报文段,该报文段首部中的 SYN=1,ACK=0,同时选择一个初始序号 seq=i。TCP 规定,SYN=1 的报文段不能携带数据,但要消耗掉一个序号。这时,TCP 客户进程进入 SYN—SENT(同步已发送)状态,这是 TCP 连接的第一次握手。

  • 第二次握手:服务端收到客户端发来的请求报文后,如果同意建立连接,则向客户端发送确认。确认报文中的 SYN=1,ACK=1,确认号 ack=i+1,同时为自己选择一个初始序号 seq=j。同样该报文段也是 SYN=1 的报文段,不能携带数据,但同样要消耗掉一个序号。这时,TCP 服务端进入 SYN—RCVD(同步收到)状态,这是 TCP 连接的第二次握手。

  • 第三次握手:TCP 客户端进程收到服务端进程的确认后,还要向服务端给出确认。确认报文段的 ACK=1,确认号 ack=j+1,而自己的序号为 seq=i+1。TCP 的标准规定,ACK 报文段可以携带数据,但如果不携带数据则不消耗序号,因此,如果不携带数据,则下一个报文段的序号仍为 seq=i+1。这时,TCP 连接已经建立,客户端进入 ESTABLISHED(已建立连接)状态。这是 TCP 连接的第三次握手,可以看出第三次握手客户端已经可以发送携带数据的报文段了。

知乎上一生动的段子:

  • 第一次握手:约吗
  • 第二次握手:约
  • 第三次握手:那我出发了

四次挥手

四次挥手

浏览器缓存

浏览器缓存知识小结及应用

强缓存

命中缓存后返回 200。

字段:

  • expires
  • cache-control

禁止缓存:Cache-Control: no-cache, no-store, must-revalidate

缓存静态资源 Cache-Control:public, max-age=31536000

  • pragma

建议只在需要兼容 HTTP/1.0 客户端的场合下应用 Pragma 首部。

强缓存

要注意,强缓存通常都是针对静态资源使用,动态资源需要慎用。

这里回溯一下之前项目中碰到的缓存问题。在之前的项目中发送 Ajax 请求时,我们通过了在请求的 URL 后面添加时间戳破缓存,这里解释一下原理。

浏览器缓存是基于 url 进行缓存的,如果页面允许缓存,则在一定时间内(缓存时效时间前)再次访问相同的 URL,浏览器就不会再次发送请求到服务器端,而是直接从缓存中获取指定资源。

1
axios.get(`/user/check?timestamp=${new Date().getTime()}`);

关于缓存更新请看这里:大公司里怎样开发和部署前端代码?

协商缓存

命中缓存后返回 304。

字段:

  • Last-Modified
  • If-Modified-Since

如果 Last-Modified 比 If-Modified-Since 晚,就从服务器端拉取数据

  • ETag
  • If-None-Match

如果 If-None-Match 和 Etag 不一致时,从服务器端拉取数据

协商缓存

浏览器行为对缓存的影响

  1. 当 ctrl+f5 强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;

  2. 当 f5 刷新网页时,跳过强缓存,但是会检查协商缓存;

load 事件和 DOMContentLoaded 事件的先后

  • 当 DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括样式表,图片。 (譬如如果有 async 加载的脚本就不一定完成)

  • 当 onload 事件触发时,页面上所有的 DOM,样式表,脚本,图片都已经加载完成了。 (渲染完毕了)

DOMContentLoaded -> load

location.Reload()和location.href 区别

window.location.reload()和window.location.href=””的区别

React

React性能提升

  • 避免不必要的render
  • 无状态组件

Immutable

Immutable 详解及 React 中实践

计算机网络

HTTP请求方法

HTTP请求方法:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE

前端构建工具

代码片段

数组随机排序

方法一:数组元素交换位置

1
2
3
4
5
6
7
8
function randSort(arr) {
for(let i = 0,cosnt len = arr.length; i < len; i++) {
const randomIndex = parseInt(Math.random()*len)
let temp = arr[randomIndex]
arr[randomIndex] = arr[i]
arr[i] = temp
}
}

方法二:将原数组数据随机塞入新数组

1
2
3
4
5
6
7
8
9
function randSort(arr) {
const mixedArray = []
while(arr.length > 0) {
const randomIndex = parseInt(Math.random()*arr.length)
mixedArray.push(arr[randomIndex])
arr.splice(randomIndex, 1)
}
return mixedArray
}

方法三:sort

1
arr.sort(() => Math.random() - 0.5)

数组去重

方法一:set

1
const unique = arr => [...new Set(arr)]

方法二:利用key的唯一性进行去重

1
2
3
4
5
6
const unique = arr => {
const seen = {}
return arr.filter((item, index, arr) => {
return seen[item] ? false : seen[item] = true
})
}

数组降维

concat

1
2
3
4
5
6
7
function reduceDimension(arr) {
const reduced = []
for (var i = 0; i < arr.length; i++){
reduced = reduced.concat(arr[i]);
}
return reduced
}

apply 和 concat

1
2
3
function reduceDimension(arr) {
return Array.prototype.concat.apply([], arr)
}

全排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function permutate(str) {
const result = []
if (str.length > 1) {
var left = str[0]
var rest = str.slice(1, str.length)
var preResult = permutate(rest)
for(var i=0; i<preResult.length; i++) {
for(var j=0; j<preResult[i].length; j++) {
var tmp = preResult[i],slice(0, j) + left + preResult[i].slice(j, preResult[i].length);
result.push(tmp);
}
}
} else if (str.length === 1) {
return [str]
}
return result
}
如何优雅的要钱