找最大值、重复元素.md

一维数组中寻找最大值

let array=[……]

1、for循环遍历

for循环是最直接的方法,但也是时间复杂度最高的方法。O(n)

1
2
3
4
5
6
let array = [5, 7, 89, 2, 4]
let max = array[0];
for (let i = 0; i < array.length; i++) {
max = (max >= array[i]) ? max : array[i];
}
console.log(max); //89

2、Math.max

推荐的方法,但是要注意Math.max()对象中传递的单个参数,不能传递整个数组,此时便需要用apply方法将数据对象转化为单个参数对象。这种算法的时间负责度为多少??

1
2
3
let array = [5, 7, 89, 2, 4]
let max = Math.max.apply(array, array);
console.log(max); //89

但是在ES6中,有一个新的API,扩展运算符…,将一个数组转为用逗号分隔的参数序列,所以上式还有一个更简单的替代apply的方法

1
let max = Math.max(...array)

顺便可以区分一下apply、call、bind之间的区别。
call传递的是单个参数:call(this,arg1,arg2,arg3...)
apply可以传递的是一个数组: apply(this,arguments[])
bind只传递一个对象:bind(this)

3、Array.sort()排序之后再取最后一位

这个办法的时间复杂度就是视具体浏览器JS引擎以及数组长度而定了。

1
2
3
let array = [5, 7, 89, 2, 4]
let newArr= array.sort();
console.log(newArr[newArr.length-1]); //89

一维数组寻找重复率最高的元素

使用hash

让object.key=元素,value=出现次数,遍历对象,找到value中最大值,然后将对应的key值返回就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//找出数组中出现最多的元素
let arr = ['a', 'b', 'c', 'a', 'a', 'b'];
let res = {};

//遍历数组,生成keys
for (let i = 0; i < arr.length; i++) {
if (!res[arr[i]]) {
res[arr[i]] = 1
} else {
res[arr[i]]++;
}
}

//遍历对象,找出最大的值
//这里使用Object.keys遍历
let keys = Object.keys(res);
let maxNum = 0, max;
for (let i = 0; i < keys.length; i++) {
if (maxNum < res[keys[i]]) {
maxNum = res[keys[i]];
max = keys[i]
}
}
console.log(max)
使用空间换时间

找到找到字符串中出现频率最高的字符

这个问题和在数组中寻找出现频率最高的元素其实是同一个问题,区别在于,一个是字符,一个是数组。遍历的方式不一样而已。

对于for循环遍历,字符串具有length属性,上面的方法完全适用。

同源策略到前端跨域.md

同源策略到前端跨域

前言:之前学习源生ajax时,遇到过ajax跨越问题,当时看红宝书知道CROS解决,一直在前端设置,不成功,一直以为是我哪里没有设置好,到后面才发现其实这个解决是需要在服务器端返回信息里面添加返回头Response Header,让浏览器允许访问跨域资源。实际上, 浏览器不会拦截不合法的跨域请求, 而是拦截了他们的响应, 因此即使请求不合法, 很多时候, 服务器依然收到了请求。后面看了路易斯的ajax知识体系又知道了有关ajax跨域还有其他的跨域解决方案,我以为我懂了,但是都没有去验证,直到过了一段时间,再看到这篇博文以及路易斯关于同源策略到前端跨域这边文章时,自己对跨域的了解其实很片面。今天好好总结一下有关跨域的整个由来以及解决方案。

同源策略

同源策略 (Same-Origin Policy) 最早由 Netscape 公司提出, 所谓同源就是要求, 域名, 协议, 端口相同. 非同源的脚本不能访问或者操作其他域的页面对象(如DOM等). 作为著名的安全策略, 虽然它只是一个规范, 并不强制要求, 但现在所有支持 javaScript 的浏览器都会使用这个策略. 以至于该策略成为浏览器最核心最基本的安全功能, 如果缺少了同源策略, web的安全将无从谈起.

网上的一个栗子:相对 http://store.company.com/dir/page.html 同源检测的示例:

URL 结果 原因
http://store.company.com/dir/inner/another.html 成功 同一域名
http://store.company.com/dir2/other.html 成功 同一域名下不同文件夹
https://store.company.com/secure.html 失败 不同的协议 ( https )
http://store.company.com:81/dir/etc.html 失败 不同的端口 ( 81 )
http://news.company.com/dir/other.html 失败 不同的主机 ( news )

同源策略要求三同, 即:同域,同协议,同端口.

  • 同域即host相同, 顶级域名, 一级域名, 二级域名, 三级域名等必须相同, 且域名不能与 ip 对应;
  • 同协议要求, http与https协议必须保持一致;
  • 同端口要求, 端口号必须相同.

在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以加载跨域资源,而不受同源限制,但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

跨域访问

关于解决跨域访问的方法, 一共收集到以下几种,CROS,取消浏览器安全校验,JSONP实际操作过,其他的方法来源于网络,并没有验证。

1、主域相同的跨域

1.1、document.domain

document.domain的场景只适用于不同子域的框架间的交互,及主域必须相同的不同源。

通过修改document的domain属性,我们可以在域和子域或者不同的子域之间通信(即它们必须在同一个一级域名下). 同域策略认为域和子域隶属于不同的域,比如a.com和 script.a.com是不同的域,这时,我们无法在a.com下的页面中调用script.a.com中定义的JavaScript方法。但是当我们把它们document的domain属性都修改为a.com,浏览器就会认为它们处于同一个域下,那么我们就可以互相获取对方数据或者操作对方DOM了。

比如, 我们在 www.a.com/a.html 下, 现在想获取 www.script.a.com/b.html, 即主域名相同, 二级域名不同. 那么可以这么做:

1
2
3
4
5
6
7
8
9
10
document.domain = 'a.com';
var iframe = document.createElement('iframe');
iframe.src = 'http://www.script.a.com/b.html';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.addEventListener('load',function(){
//TODO 载入完成时做的事情
//var _document = iframe.contentWindow.document;
//...
},false);

注:浏览器单独保存端口号。任何的赋值操作,包括document.domain = document.domain都会以null值覆盖掉原来的端口号。因此,赋值时必须带上端口号,确保端口号不会为null.


2、完全不同源的跨域(两个页面之间的通信)

2.1 通过location.hash跨域

假设域名a.com下的文件cs1.html要和jianshu.com域名下的cs2.html传递信息。
1、cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向jianshu.com域名下的cs2.html页面。
2、cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据。
3、同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值。

注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe。

优点:1.可以解决域名完全不同的跨域。2.可以实现双向通讯。
缺点:location.hash会直接暴露在URL里,并且在一些浏览器里会产生历史记录,数据安全性不高也影响用户体验。另外由于URL大小的限制,支持传递的数据量也不大。有些浏览器不支持onhashchange事件,需要轮询来获知URL的变化。

2.2通过window.name跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。

1
2
3
window.name = data;//父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入。        
location = 'http://parent.url.com/xxx.html';//接着,子窗口跳回一个与主窗口同域的网址。
var data = document.getElementById('myFrame').contentWindow.name。//然后,主窗口就可以读取子窗口的window.name了。

如果是与iframe通信的场景就需要把iframe的src设置成当前域的一个页面地址。

这个方式非常适合单向的数据请求,而且协议简单、安全. 不会像JSONP那样不做限制地执行外部脚本.

2.3、通过window.postMessage跨域

ES5新增的 postMessage() 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递.

语法: postMessage(data,origin)

data: 要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候建议使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果.

origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/“。

父窗口和子窗口都可以通过message事件,监听对方的消息。message事件的事件对象event,提供以下三个属性:

  • 1、event.source:发送消息的窗口。
  • 2、event.origin: 消息发向的网址。
  • 3、event.data:消息内容。

父页面发送消息:

1
window.frames[0].postMessage('message', origin)

iframe接受消息:

1
2
3
4
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;//若消息源不是父页面则退出
//TODO ...
});

2.4、Access Control

此跨域方法目前只在很少的浏览器中得以支持, 这些浏览器可以发送一个跨域的HTTP请求(Firefox, Google Chrome等通过XMLHTTPRequest实现, IE8下通过XDomainRequest实现), 请求的响应必须包含一个Access- Control-Allow-Origin的HTTP响应头, 该响应头声明了请求域的可访问权限. 例如baidu.com对google.com下的getUsers.php发送了一个跨域的HTTP请求(通过ajax), 那么getUsers.php必须加入如下的响应头:

1
header("Access-Control-Allow-Origin: http://www.baidu.com");//表示允许baidu.com跨域请求本文件

2.5、flash URLLoder

flash有自己的一套安全策略, 服务器可以通过crossdomain.xml文件来声明能被哪些域的SWF文件访问, SWF也可以通过API来确定自身能被哪些域的SWF加载. 当跨域访问资源时, 例如从域 a.com 请求域 b.com上的数据, 我们可以借助flash来发送HTTP请求.

  • 首先, 修改域 b.com上的 crossdomain.xml(一般存放在根目录, 如果没有需要手动创建) , 把 a.com 加入到白名单;
1
2
3
4
5
<?xml version="1.0"?>
<cross-domain-policy>
<site-control permitted-cross-domain-policies="by-content-type"/>
<allow-access-from domain="a.com" />
</cross-domain-policy>
  • 其次, 通过Flash URLLoader发送HTTP请求, 拿到请求后并返回;
  • 最后, 通过Flash API把响应结果传递给JavaScript.

Flash URLLoader是一种很普遍的跨域解决方案,不过需要支持iOS的话,这个方案就不可行了.

2.6、使用代理

虽然ajax和iframe受同源策略限制, 但服务器端代码请求, 却不受此限制, 我们可以基于此去伪造一个同源请求, 实现跨域的访问. 如下便是实现思路:

  1. 请求同域下的web服务器;
  2. web服务器像代理一样去请求真正的第三方服务器;
  3. 代理拿到数据过后, 直接返回给客户端ajax. 这样, 我们便拿到了跨域数据.

但是这种方法成本高,操作麻烦,不推荐

3、Ajax请求不同源的跨域跨域

3.1、通过CROS跨域

CORS是一个W3C(World Wide Web)标准, 全称是跨域资源共享(Cross-origin resource sharing).它允许浏览器向跨域服务器, 发出异步http请求, 从而克服了ajax受同源策略的限制. 实际上, 浏览器不会拦截不合法的跨域请求, 而是拦截了他们的响应, 因此即使请求不合法, 很多时候, 服务器依然收到了请求.(Chrome和Firefox下https网站不允许发送http异步请求除外)

简而言之, 浏览器不再一味禁止跨域访问, 而是检查目的站点的响应头域, 进而判断是否允许当前站点访问. 通常, 服务器使用以下的这些响应头域用来通知浏览器:

  • Access-Control-Allow-Origin: 指定允许哪些源的网页发送请求.

  • Access-Control-Allow-Credentials: 指定是否允许cookie发送.

  • Access-Control-Allow-Methods: 指定允许哪些请求方法.

  • Access-Control-Allow-Headers: 指定允许哪些常规的头域字段, 比如说 Content-Type.

  • Access-Control-Expose-Headers: 指定允许哪些额外的头域字段, 比如说 X-Custom-Header.

  • Access-Control-Max-Age: 指定preflight OPTIONS请求的有效期, 单位为秒.

CORS请求分为两种, ① 简单请求; ② 非简单请求.

满足如下两个条件便是简单请求, 反之则为非简单请求.(CORS请求部分摘自阮一峰老师博客)

1) 请求是以下三种之一:

  • HEAD
  • GET
  • POST

2) http头域不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type字段限三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
3.1.1 简单请求

浏览器将发送一次http请求, 同时在Request头域中增加 Origin 字段, 用来标示请求发起的源, 服务器根据这个源采取不同的响应策略. 若服务器认为该请求合法, 那么需要往返回的 HTTP Response 中添加 Access-Control-_ 等字段._

一个栗子:假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源。以下的 JavaScript 代码应该会在 foo.example 上执行:

1
2
3
4
5
6
7
8
9
10
//foo.example
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//让我们看看,在这个场景中,浏览器会发送什么的请求到服务器,而服务器又会返回什么给浏览器:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130
Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example //该请求来自于 http://foo.exmaple。
//以上是浏览器发送请求

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: * //这表明服务器接受来自任何站点的跨站请求。如果设置为http://foo.example。其它站点就不能跨站访问 http://bar.other 的资源了。
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
//以上是服务器返回信息给浏览器

通过使用 Origin 和 Access-Control-Allow-Origin 就可以完成最简单的跨站请求。不过服务器需要把 Access-Control-Allow-Origin 设置为 * 或者包含由 Origin 指明的站点。

3.1.2 非简单请求(预请求)

当请求具备以下条件,就会被当成预请求(非简单请求)处理:

(1)请求以 GET, HEAD 或者 POST 以外的方法发起请求。或者,使用 POST,但请求数据为 application/x-www-form-urlencoded, multipart/form-data 或者 text/plain 以外的数据类型。比如说,用 POST 发送数据类型为 application/xml 或者 text/xml 的 XML 数据的请求。

(2)使用自定义请求头(比如添加诸如 X-PINGOTHER)
对于非简单请求,浏览器将发送两次http请求. 第一次为preflight预检(Method: OPTIONS),主要验证来源是否合法. 值得注意的是:OPTION请求响应头同样需要包含 Access-Control-* 字段等. 第二次才是真正的HTTP请求. 所以服务器必须处理OPTIONS应答(通常需要返回20X的状态码, 否则xhr.onerror事件将被触发).

一个例子:使用了自定义请求头的非简单请求

1
2
3
4
5
6
7
8
9
10
11
12
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}Arun';
function callOtherDomain(){
if(invocation){
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}

以 XMLHttpRequest 创建了一个 POST 请求,为该请求添加了一个自定义请求头(X-PINGOTHER: pingpong),并指定数据类型为 application/xml。所以,该请求是一个“预请求”形式的跨站请求。浏览器使用一个 OPTIONS 发送了一个“预请求”。

假设服务器成功响应返回部分信息如下:

1
2
3
4
Access-Control-Allow-Origin: http://foo.example //表明服务器允许http://foo.example的请求
Access-Control-Allow-Methods: POST, GET, OPTIONS //表明服务器可以接受POST, GET和 OPTIONS的请求方法
Access-Control-Allow-Headers: X-PINGOTHER //传递一个可接受的自定义请求头列表。服务器也需要设置一个与浏览器对应。否则会报 Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers in preflight response 的错误
Access-Control-Max-Age: 1728000 //告诉浏览器,本次“预请求”的响应结果有效时间是多久。在上面的例子里,1728000秒代表着20天内,浏览器在处理针对该服务器的跨站请求,都可以无需再发送“预请求”,只需根据本次结果进行判断处理。
3.1.3 附带凭证信息的请求

XMLHttpRequest 和访问控制功能,最有趣的特性就是,发送凭证请求(HTTP Cookies和验证信息)的功能。一般而言,对于跨站请求,浏览器是不会发送凭证信息的。但如果将 XMLHttpRequest 的一个特殊标志位withCredentials设置为true,浏览器就将允许该请求的发送。

1
2
3
4
5
6
7
8
9
10
11
//http://foo.example站点的脚本向http://bar.other站点发送一个GET请求,并设置了一个Cookies值。脚本代码如下:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
}

第七行代码将 XMLHttpRequest 的withCredentials标志设置为true,从而使得Cookies可以随着请求发送。因为这是一个简单的GET请求,所以浏览器不会发送一个“预请求”。但是,如果服务器端的响应中,如果没有返回Access-Control-Allow-Credentials: true的响应头,那么浏览器将不会把响应结果传递给发出请求的脚本程序,以保证信息的安全。

假设服务器成功响应返回部分信息应该如下:

1
2
3
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT

如果bar.other的响应头里没有Access-Control-Allow-Credentials:true,则响应会被忽略.。

特别注意:

给一个带有withCredentials的请求发送响应的时候,服务器端必须指定允许请求的域名,不能使用“*”。上面这个例子中,如果响应头是这样的 Access-Control-Allow-Origin:* ,则响应会失败。

3.2、 JSONP跨域

基本原理:网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。例子如下:

1
2
3
4
5
6
7
8
9
function todo(data){
console.log('The author is: '+ data.name);
}
var script = document.createElement('script');
script.src = 'http://www.jianshu.com/author?callback=todo';//向服务器www.jianshu.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字。
document.body.appendChild(script);
//服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
todo({"name": "fewjq"});
//由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了todo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象。

优点:简单适用,老式浏览器全部支持,服务器改造小。不需要XMLHttpRequest或ActiveX的支持。

缺点:只支持GET请求。

3.3、WebSocket跨域

WebSocket 本质上是一个基于TCP的协议, 它的目标是在一个单独的持久链接上提供全双工(full-duplex), 双向通信, 以基于事件的方式, 赋予浏览器实时通信能力. 既然是双向通信, 就意味着服务器端和客户端可以同时发送并响应请求, 而不再像HTTP的请求和响应. (同源策略对 web sockets 不适用)

原理: 为了建立一个WebSocket连接,客户端浏览器首先要向服务器发起一个HTTP请求, 这个请求和通常的HTTP请求不同, 包含了一些附加头信息, 其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的HTTP请求, 服务器端解析这些附加的头信息然后产生应答信息返回给客户端, 客户端和服务器端的WebSocket连接就建立起来了, 双方就可以通过这个连接通道自由的传递信息, 并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接.

3.4、Comet跨域

在WebSocket出现之前, 很多网站为了实现实时推送技术, 通常采用的方案是轮询(Polling)和Comet技术, Comet又可细分为两种实现方式, 一种是长轮询机制, 一种称为流技术, 这两种方式实际上是对轮询技术的改进, 这些方案带来很明显的缺点, 需要由浏览器对服务器发出HTTP request, 大量消耗服务器带宽和资源. 面对这种状况, HTML5定义了WebSocket协议, 能更好的节省服务器资源和带宽并实现真正意义上的实时推送.

3.5、图像ping跨域

图像ping跨域请求技术是使用 <img> 标签。我们知道,一个网页可以从任何网页中加载图像,不
用担心跨域不跨域。

图像ping有两个主要的缺点,1、只能发送GET请求;2、无法访问服务器相应文本

3.6、浏览器解除安全控制,允许跨域

在实际开发过程中,有时候经常遇到这样一种情况:开发过程中会涉及到跨域问题,前端需要向服务请求某个接口,但是由于不同源,浏览器拦截响应,在上线之后项目便在同一个源,这种情况为了快速开发,可以将浏览器安全检查关闭,允许获取跨域资源。
禁止浏览器跨域安全检查(这里以chrome为例)

1
"C:\Users\UserName\AppData\Local\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir

Chrome 跨域 disable-web-security 关闭安全策略

文章参考摘录来源:
前端跨域问题及解决方案
路易斯-《由同源策略到前端跨域》
路易斯-《ajax知识大梳理》

一些常用的JS方法.md

filter() 方法实例

javascript filter() 方法使用指定的函数测试所有元素,并创建一个包含所有通过测试的元素的新数组。

1
arr.filter(callback,[ thisArg])

indexOf() 方法

可返回某个指定的字符串值在字符串中首次出现的位置。
语法

1
stringObject.indexOf(searchvalue,fromindex)

assign() 方法

assign() 方法可加载一个新的文档。

1
location.assign(URL)

Object.assign()方法

特点:浅拷贝、对象属性的合并

var nObj = Object.assign({},obj,obj1);//花括号叫目标对象,后面的obj、obj1是源对象。对象合并是指:将源对象里面的属性添加到目标对象中去,若两者的属性名有冲突,后面的将会覆盖前面的

文件流转BinaryString

1
2
3
4
5
6
7
8
9
10
//文件流转BinaryString
function fixdata(data) {
var o = "",
l = 0,
w = 10240;
for (; l < data.byteLength / w; ++l)
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)));
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
return o;
}

unshift() 方法

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
语法:

1
arrayObject.unshift(newelement1,newelement2,....,newelementX)

参数 描述
newelement1 必需。向数组添加的第一个元素。
newelement2 可选。向数组添加的第二个元素。
newelementX 可选。可添加若干个元素。

JS引擎-以V8为例.md

同步:一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步:进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
进程:狭义上,就是正在运行的程序的实例。广义上,进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
线程:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位。指运行中的程序的调度单位。
单线程:单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。单线程就是进程里只有一个线程。
多线程:在单个程序中同时运行多个线程完成不同的工作,称为多线程。

JS实现深度复制.md

JS深复制是在阿里一面的过程中遇到的,以前都不知道,所以当时一脸懵逼,回来赶紧google,发现邹大大以前写过关于深复制的实现,这里参考他的文章:深入剖析 JavaScript 的深复制

浅复制

复制的是存在栈内存里面的引用指针,并不是存放在堆内存中的内容,当对一个对象下面的属性值进行修改时,所有的都会变化。

1
2
3
4
5
6
7
8
9
10
/** 浅复制 **/
let person1 = {
'name': 'Jack',
'age': 29
};
let person2 = person1;
person2.name = "Kai";

console.log(person1.name); //'Kai'
console.log(person2.name); //'Kai'

深复制

深复制想实现把对象里面属性值也给复制,当修改复制后的对象某个属性值时,原对象对应的属性值不会被修改。

实现方法

  1. JSON的全局对象parsestringify方法。
  2. jQuery的$.clone()$.extend()方法。
  3. Underscored的_.clone()
  4. lodash的_.clone() / _.cloneDeep()
  5. 邹润阳建议的拥抱未来的深复制方法,直接定义在prototype上面。

JSON的全局对象parsestringify方法。

1
2
3
function deepClone(source){
return JSON.parse(JSON.stringify(source));
}

例子:

1
2
3
4
5
6
7
8
9
10
11
/** 深复制 **/
let person1 = {
'name': 'Jack',
'age': 29
};

let person2 = JSON.parse(JSON.stringify(person1));
person2.name = "Kai";

console.log(person1.name); //'Jack'
console.log(person2.name); //'Kai'

上面这种方法好处是非常简单易用,,对于Number, String,Obejct等来说基本实现。但是坏处也显而易见,对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor,也就是深复制之后,无论这个对象原本的构造函数是什么,在深复制之后都会变成Object。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** 深复制 **/
let person1 = {
'name': 'Jack',
'age': 29,
'fn':function (){
console.log('fn')
},
'reg':new RegExp('e')
};

let person2 = JSON.parse(JSON.stringify(person1));
person2.name = "Kai";

console.log(person1.name); //'Jack'
console.log(person2.name); //'Kai'
console.log(person2.fn); //undefined
console.log(person2.reg); //Object

jQuery Underscored lodash第三方插件实现

现在不做介绍,以后用的的时候再补充进来

邹润阳–拥抱未来的深复制方法

Javascript编程风格.md

来源:《Javascript编程风格》

大括号

规则1:表示区块起首的大括号,不要另起一行。

圆括号

规则2:调用函数的时候,函数名与左括号之间没有空格。

规则3:函数名与参数序列之间,没有空格。

规则4:所有其他语法元素与左括号之间,都有一个空格。

分号

规则5:不要省略句末的分号。

with语句

规则6:不要使用with语句。

=====

规则7:不要使用”相等”(==)运算符,只使用”严格相等”(===)运算符。

语句的合并

规则8:不要将不同目的的语句,合并成一行。尤其是赋值语句。

变量声明

规则9:所有变量声明都放在函数的头部。

规则10:所有函数都在使用之前定义。

全局变量

规则11:避免使用全局变量;如果不得不使用,用大写字母表示变量名,比如UPPER_CASE。

new命令

规则12:不要使用new命令,改用Object.create()命令。

规则13:建构函数的函数名,采用首字母大写(InitialCap);其他函数名,一律首字母小写。

自增和自减运算符

规则14:不要使用自增(++)和自减(–)运算符,用+=和-=代替。

区块

规则15:总是使用大括号表示区块。

this对象.md

参考:阮一峰《Javascript的this用法》

追梦子《彻底理解js中this的指向,不必硬背

1、纯粹的函数调用与作为对象方法的调用

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁实际上this的最终指向的是那个调用它的对象。这句话并不完全对,但是大多数时候可以用这种方法去判断this指向。

一般情况下,this指向全局对象window,因为是全局window调用

这里的a()是由window.a()调用的,所以this指向window

1
2
3
4
5
6
function a(){
var user = "追梦子";
console.log(this.user); //undefined
console.log(this); //Window
}
a();

通过其他对象调用,this指向调用对象

1
2
3
4
5
6
7
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
o.fn();

这里 fn()中的this由对象o调用,则this指向对象o。

如有多层对象,this指向它上一级的对象。

1
2
3
4
5
6
7
8
9
10
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();

此时this指向上一级队形b而不是指向对象o。

总结起来三种情况:

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

一个特殊的例子

1
2
3
4
5
6
7
8
9
10
11
12
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();

这里的this并没有指向对象b,而是window,因为最后的调用是window.j()。

作为纯粹的函数调用其实就是window调用,其this对象指向window,改变的欧式全局属性

作为对象方法调用,this就指向这个上级对象。

2、构造函数中的this

1
2
3
4
5
function Fn(){
this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子

这里这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代

3、使用apply() call() bind() 改变this指向

掌握三种方法的用法,了解三种方法的区别即可
call传递的是单个参数:call(this,arg1,arg2,arg3...)
apply可以传递的是一个数组: apply(this,arguments[])
bind只传递一个对象:bind(this)

几种特殊的情况

1、当this遇到return时

如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例

请看例子

1
2
3
4
5
6
7
8
9
function fn()  
{
this.user = '追梦子';
return {};
//或者
//return function(){};
}
var a = new fn;
console.log(a.user); //undefined
1
2
3
4
5
6
7
8
function fn()  
{
this.user = '追梦子';
return 1;
//或者undefined
}
var a = new fn;
console.log(a.user); //追梦子

又一个特殊的情况

1
2
3
4
5
6
7
function fn()  
{
this.user = '追梦子';
return null;
}
var a = new fn;
console.log(a.user); //追梦子

null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

2、闭包中的this对象

闭包的执行环境具有全局性,所以在闭包中,this一般指向window

String对象.md

JavaScript String对象

参考:

JavaScript字符串所有API全解密

String 对象属性

属性 描述
constructor 对创建该对象的函数的引用
length 字符串的长度
prototype 允许您向对象添加属性和方法

String 对象方法

方法 描述
charAt() 返回在指定位置的字符。
charCodeAt() 返回在指定的位置的字符的 Unicode 编码。
fromCharCode() 将 Unicode 编码转为字符。
toLowerCase() 把字符串转换为小写。
toUpperCase() 把字符串转换为大写。
trim() 去除字符串两边的空白
concat() 连接两个或更多字符串,并返回新的字符串。如果有多个空格,则视为一个
indexOf() 返回某个指定的字符串值在字符串中首次出现的位置。
lastIndexOf() 从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。
search() 查找与正则表达式相匹配的值。
match() 查找找到一个或多个正则表达式的匹配。这个蛮有用
replace() 在字符串中查找匹配的子串, 并替换与正则表达式匹配的子串。
split() 把字符串分割为字符串数组。注意参数为负的情况
slice(start,end) 提取字符串的片断,并在新的字符串中返回被提取的部分。
substr(start,length) 从起始索引号提取字符串中指定数目的字符。
substring(from , to) 提取字符串中两个指定的索引号之间的字符。
valueOf() 返回某个字符串对象的原始值。

String HTML 包装方法

方法 描述
anchor() 创建 HTML 锚。
big() 用大号字体显示字符串。
blink() 显示闪动字符串。
bold() 使用粗体显示字符串。
fixed() 以打字机文本显示字符串。
fontcolor() 使用指定的颜色来显示字符串。
fontsize() 使用指定的尺寸来显示字符串。
italics() 使用斜体显示字符串。
link() 将字符串显示为链接。
small() 使用小字号来显示字符串。
strike() 用于显示加删除线的字符串。
sub() 把字符串显示为下标。
sup() 把字符串显示为上标。

charAt

charAt() 方法返回字符串中指定位置的字符。

语法:str.charAt(index) index 为字符串索引(取值从0至length-1),如果超出该范围,则返回空串。

charCodeAt

charCodeAt() 返回指定索引处字符的 Unicode 数值。

语法:str.charCodeAt(index)。index 为一个从0至length-1的整数。如果不是一个数值,则默认为 0,如果小于0或者大于字符串长度,则返回 NaN。

concat

concat() 方法将一个或多个字符串拼接在一起,组成新的字符串并返回。

语法:str.concat(string2, string3, …). concat 的性能表现不佳,强烈推荐使用赋值操作符(+或+=)代替 concat

indexOf / lastIndexOf

indexOf() 方法用于查找子字符串在字符串中首次出现的位置,没有则返回 -1。lastIndexOf 则从右往左查找,其它与前者一致

语法:str.indexOf(searchValue [, fromIndex=0]),str.lastIndexOf(searchValue [, fromIndex=0])

localeCompare

localeCompare() 方法用来比较字符串,如果指定字符串在原字符串的前面则返回负数,否则返回正数或0,其中0 表示两个字符串相同

match

match() 方法用于测试字符串是否支持指定正则表达式的规则,即使传入的是非正则表达式对象,它也会隐式地使用new RegExp(obj)将其转换为正则表达式对象。

语法:str.match(regexp).该方法返回包含匹配结果的数组,如果没有匹配项,则返回 null。

  • 若正则表达式没有 g 标志,则返回同 RegExp.exec(str) 相同的结果。而且返回的数组拥有一个额外的 input 属性,该属性包含原始字符串,另外该数组还拥有一个 index 属性,该属性表示匹配字符串在原字符串中索引(从0开始)。
  • 若正则表达式包含 g 标志,则该方法返回一个包含所有匹配结果的数组,没有匹配到则返回 null。

replace 使用最多的方法

该方法并不改变调用它的字符串本身,而只是返回替换后的字符串.

语法: str.replace( regexp | substr, newSubStr | function[, flags] )

简单概括,replace拥有两个参数,第一个是需要替换的字符串或者正则表达式;
第二个是新的字符串或者一个function,这样参数便有四种组合.

  • regexp: 一个 RegExp 对象. 该正则所匹配的内容会被第二个参数的返回值替换掉。
  • substr: 一个要被 newSubStr 替换的字符串.
  • newSubStr: 替换掉第一个参数在原字符串中的匹配部分. 该字符串中可以内插一些特殊的变量名.
  • function: 一个用来创建新子字符串的函数, 该函数的返回值将替换掉第一个参数匹配到的结果. 该函数的参数描述请参考 指定一个函数作为参数 小节.
  • flags: 注意:flags 参数在 v8 内核(Chrome and NodeJs)中不起作用. 方法中使用 flags 参数不是符合标准的并且不赞成这样做.

search() 方法用于测试字符串对象是否包含某个正则匹配,相当于正则表达式的 test 方法,且该方法比 match() 方法更快。如果匹配成功,search() 返回正则表达式在字符串中首次匹配项的索引,否则返回-1。

语法:str.search(regexp)

slice

slice() 方法提取字符串的一部分,并返回新的字符串。该方法有些类似 Array.prototype.slice 方法。

语法:str.slice(start, end)

split

split() 方法把原字符串分割成子字符串组成数组,并返回该数组

语法:str.split(separator, limit)

两个参数均是可选的,其中 separator 表示分隔符,它可以是字符串也可以是正则表达式。如果忽略 separator,则返回的数组包含一个由原字符串组成的元素。如果 separator 是一个空串,则 str 将会被分割成一个由原字符串中字符组成的数组。limit 表示从返回的数组中截取前 limit 个元素,从而限定返回的数组长度。

substr

substr() 方法返回字符串指定位置开始的指定数量的字符。

语法:str.substr(start[, length])

subString

substring() 方法返回字符串两个索引之间的子串。

语法:str.substring(indexA[, indexB])
indexA、indexB 表示字符串索引,其中 indexB 可选,如果省略,则表示返回从 indexA 到字符串末尾的子串。

  • 若 indexA == indexB,则返回一个空字符串;
  • 若 省略 indexB,则提取字符一直到字符串末尾;
  • 若 任一参数小于 0 或 NaN,则被当作 0;
  • 若 任一参数大于 length,则被当作 length。
  • 如果 indexA > indexB,则 substring 的执行效果就像是两个参数调换一般

toLocaleLowerCase、toLocaleUpperCase

toLocaleLowerCase() 方法返回调用该方法的字符串被转换成小写的值,转换规则根据本地化的大小写映射。而toLocaleUpperCase() 方法则是转换成大写的值。

toLowerCase 、toUpperCase

这两个方法分别表示将字符串转换为相应的小写,大写形式,并返回

trim

trim() 方法清除字符串首尾的空白并返回。

includes(ES6)

includes() 方法基于ECMAScript 2015(ES6)规范,它用来判断一个字符串是否属于另一个字符。如果是,则返回true,否则返回false。
语法:str.includes(subString [, position])

endsWith(ES6) 、startsWith(ES6)

endsWith() 方法基于ECMAScript 2015(ES6)规范,它基本与 contains() 功能相同,不同的是,它用来判断一个字符串是否是原字符串的结尾。若是则返回true,否则返回false。

小结

  • substr 和 substring,都是两个参数,作用基本相同,两者第一个参数含义相同,但用法不同,前者可为负数,后者值为负数或者非整数时将隐式转换为0。前者第二个参数表示截取字符串的长度,后者第二个参数表示截取字符串的下标;同时substring第一个参数大于第二个参数时,执行结果同位置调换后的结果。
  • search方法与indexOf方法作用基本一致,都是查询到了就返回子串第一次出现的下标,否则返回-1,唯一的区别就在于search默认会将子串转化为正则表达式形式,而indexOf不做此处理,也不能处理正则。

字符串对象遍历

参照各种遍历方法以及使用对象

字符串回文

1
str.split('').reverse().join('');

Math对象属性及对象方法.md

Math对象属性及对象方法

API参考菜鸟教程

Math对象属性

语法:

1
Math.Attributes

E 返回算术常量 e,即自然对数的底数(约等于2.718)。
LN2 返回 2 的自然对数(约等于0.693)。
LN10 返回 10 的自然对数(约等于2.302)。
LOG2E 返回以 2 为底的 e 的对数(约等于 1.414)。
LOG10E 返回以 10 为底的 e 的对数(约等于0.434)。
PI 返回圆周率(约等于3.14159)。
SQRT1_2 返回返回 2 的平方根的倒数(约等于 0.707)。
SQRT2 返回 2 的平方根(约等于 1.414)。

Math 对象方法

方法 描述
acos(x) 返回 x 的反余弦值。
asin(x) 返回 x 的反正弦值。
atan(x) 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。
atan2(y,x) 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。注意y在前,x在后
cos(x) 返回数的余弦。
sin(x) 返回数的正弦。
tan(x) 返回角的正切。
ceil(x) 对数进行上舍入。
floor(x) 对 x 进行下舍入。
max(x,y,z,…,n) 返回 x,y,z,…,n 中的最高值。
min(x,y,z,…,n) 返回 x,y,z,…,n中的最低值。
random() 返回 0 ~ 1 之间的随机数。
round(x) 把数四舍五入为最接近的整数。
abs(x) 返回 x 的绝对值。
exp(x) 返回 E的x次幂指数。
log(x) 返回数的自然对数(底为e)。
pow(x,y) 返回 x 的 y 次幂。
sqrt(x) 返回数的平方根。