zerolhao-blog


  • Home

  • Tags

  • Categories

  • Archives

  • Sitemap

H34-AJAX

Posted on 2018-03-07 | In 饥人谷

如何发请求?

用 form 可以发请求,但是会刷新页面或新开页面
用 a 可以发 get 请求,但是也会刷新页面或新开页面
用 img 可以发 get 请求,但是只能以图片的形式展示
用 link 可以发 get 请求,但是只能以 CSS、favicon 的形式展示
用 script 可以发 get 请求,但是只能以脚本的形式运行

有没有什么方式可以实现

  1. get、post、put、delete 请求都行
  2. 想以什么形式展示就以什么形式展示

微软的突破

IE 5 率先在 JS 中引入 ActiveX 对象(API),使得 JS 可以直接发起 HTTP 请求。
随后 Mozilla、 Safari、 Opera 也跟进(抄袭)了,取名 XMLHttpRequest,并被纳入 W3C 规范

AJAX

Jesse James Garrett 讲如下技术取名叫做 AJAX:异步的 JavaScript 和 XML
Asynchronous JavaScript and XML

  1. 使用 XMLHttpRequest 发请求
  2. 服务器返回 XML 格式的字符串
  3. JS 解析 XML,并更新局部页面
    (但我们现在不同XML改用更好用的JSON了)

XMLHttpRequest实例的属性

  • readyState
    readyState是一个只读属性,用一个整数和对应的常量,表示XMLHttpRequest请求当前所处的状态。

0,对应常量UNSENT,表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用。
1,对应常量OPENED,表示send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息。
2,对应常量HEADERS_RECEIVED,表示send()方法已经执行,并且头信息和状态码已经收到。
3,对应常量LOADING,表示正在接收服务器传来的body部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息。
4,对应常量DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。

  • readyStateChange
    readyState属性的值发生改变,就会触发readyStateChange事件。
    我们可以通过onReadyStateChange属性,指定这个事件的回调函数,对不同状态进行不同处理。尤其是当状态变为4的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。

  • onreadystatechange
    onreadystatechange属性指向一个回调函数,当readystatechange事件发生的时候,这个回调函数就会调用,并且XMLHttpRequest实例的readyState属性也会发生变化。
    另外,如果使用abort()方法,终止XMLHttpRequest请求,onreadystatechange回调函数也会被调用。

  • response
    response属性为只读,返回接收到的数据体(即body部分)。
    它的类型可以是ArrayBuffer、Blob、Document、JSON对象、或者一个字符串,这由XMLHttpRequest.responseType属性的值决定。
    如果本次请求没有成功或者数据不完整,该属性就会等于null。

  • responseType
    responseType属性用来指定服务器返回数据(xhr.response)的类型。

”“:字符串(默认值)
“arraybuffer”:ArrayBuffer对象
“blob”:Blob对象
“document”:Document对象
“json”:JSON对象
“text”:字符串

text类型适合大多数情况,而且直接处理文本也比较方便,document类型适合返回XML文档的情况,blob类型适合读取二进制数据,比如图片文件。
如果将这个属性设为“json”,支持JSON的浏览器(Firefox>9,chrome>30),就会自动对返回数据调用JSON.parse()方法。也就是说,你从xhr.response属性(注意,不是xhr.responseText属性)得到的不是文本,而是一个JSON对象。

XHR2支持Ajax的返回类型为文档,即xhr.responseType=”document” 。这意味着,对于那些打开CORS的网站,我们可以直接用Ajax抓取网页,然后不用解析HTML字符串,直接对XHR回应进行DOM操作。

  • responseText
    responseText属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。
    如果服务器返回的数据格式是JSON,就可以使用responseText属性。

    1
    2
    var data = ajax.responseText;
    data = JSON.parse(data);
  • status
    status属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200。

  • statusText
    statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。

-open()
XMLHttpRequest对象的open方法用于指定发送HTTP请求的参数,它的使用格式如下,一共可以接受五个参数。

1
2
3
4
5
6
7
void open(
string method,
string url,
optional boolean async,
optional string user,
optional string password
);

method:表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。
url: 表示请求发送的网址。
async: 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。
user:表示用于认证的用户名,默认为空字符串。
password:表示用于认证的密码,默认为空字符串。

如果对使用过open()方法的请求,再次使用这个方法,等同于调用abort()。
下面发送POST请求的例子。

1
2
3
4
xhr.open('POST', encodeURI('someURL'));
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {};
xhr.send(encodeURI('dataString'));

上面方法中,open方法向指定URL发出POST请求,send方法送出实际的数据。

下面是一个同步AJAX请求的例子。

1
2
3
4
5
6
7
var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);
request.send(null);

if (request.status === 200) {
console.log(request.responseText);
}

  • send()
    send方法用于实际发出HTTP请求。如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ajax.open('GET'
    , 'http://www.example.com/somepage.php?id=' + encodeURIComponent(id)
    , true
    );

    // 等同于
    var data = 'id=' + encodeURIComponent(id));
    ajax.open('GET', 'http://www.example.com/somepage.php', true);
    ajax.send(data);

上面代码中,GET请求的参数,可以作为查询字符串附加在URL后面,也可以作为send方法的参数。
下面是发送POST请求的例子。

1
2
3
4
5
6
7
var data = 'email='
+ encodeURIComponent(email)
+ '&password='
+ encodeURIComponent(password);
ajax.open('POST', 'http://www.example.com/somepage.php', true);
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajax.send(data);

如果请求是异步的(默认为异步),该方法在发出请求后会立即返回。如果请求为同步,该方法只有等到收到服务器回应后,才会返回。

注意,所有XMLHttpRequest的监听事件,都必须在send()方法调用之前设定。

send方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。

1
2
3
4
5
6
void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);

JSON – 一门新语言

http://json.org/
它和 JavaScript的区别:

  1. 没有抄袭 function 和 undefined
  2. JSON 的字符串首尾必须是 双引号
  3. JS VS JSON
    undefined 没有
    null null
    [‘a’,’b’] [“a”,”b”]
    function fn(){} 没有
    {name: ‘zero’} {“name”: “zero”}
    ‘string’ “string”
    var a = {}
    a.self = a 搞不定(没有变量)
    {proto} 没有原型链(只是简单的哈希)

如何使用 XMLHttpRequest

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
43
44
45
46
47
myButton.addEventListener('click', (e)=>{
let request = new XMLHttpRequest()
request.open('get', '/xxx') // 配置request
request.send()
request.onreadystatechange = ()=>{
if(request.readyState === 4){
console.log('请求响应都完毕了')
console.log(request.status)
if(request.status >= 200 && request.status < 300){
console.log('说明请求成功')
console.log(typeof request.responseText)
console.log(request.responseText)
let string = request.responseText
// 把符合 JSON 语法的字符串
// 转换成 JS 对应的值
let object = window.JSON.parse(string)
// JSON.parse 是浏览器提供的
console.log(typeof object)
console.log(object)
console.log('object.note')
console.log(object.note)

}else if(request.status >= 400){
console.log('说明请求失败')
}

}
}
})

// 后台
// 后端代码
}else if(path==='/xxx'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001') // 允许http://frank.com:8001访问
response.write(`
{
"note":{
"to": "小谷",
"from": "方方",
"heading": "打招呼",
"content": "hi"
}
}
`)
response.end()

完整代码

同源策略

只有 协议+端口+域名 一模一样才允许发 AJAX 请求

  1. http://baidu.com 可以向 http://www.baidu.com 发 AJAX 请求吗 no
  2. http://baidu.com:80 可以向 http://baidu.com:81 发 AJAX 请求吗 no

浏览器必须保证
只有 协议+端口+域名 一模一样才允许发 AJAX 请求
CORS 可以告诉浏览器,我俩一家的,别阻止他

突破同源策略 === 跨域

Cross-Origin Resource Sharing
跨域资源共享

为什么 form 可以跨域而 AJAX 不行

因为原页面用form提交到另一个域名之后,原页面的脚本无法获取新页面中的内容。
所以浏览器认为这是安全的。
而AJAX是可以读取响应内容的,因此浏览器不允许你这么做。
如果你F12查看的话,会发现请求已经发送出去了,只是你拿不到响应而已。
所以浏览器这个策略的本质是,一个域名的js,在未经允许的情况下,不得读取另一个域名的内容,但浏览器并不阻止你向另一个域名发送请求。

CORS 跨域

上面例子的这句代码
服务器添加响应头
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001')
就是设置服务器允许 http://frank.com:8001 源访问(假设服务器是)http://jack.com:8002

调试小技巧

1
2
3
4
console.time()
var i = 0;
console.timeEnd()
// 会将console中间的代码执行花费的时间打印出来

参考文章:阮一峰

H33-JSONP

Posted on 2018-03-05 | In 饥人谷

数据库是什么

  1. 文件系统是一种数据库
  2. MySQL 是一种数据库
    只要能长久的存数据,就是数据库

用数据库做加减法(更改用户账户余额)

https://github.com/zerolhao/node-demo/tree/master/A33-lessonCode

局部刷新怎么做

不返回html,返回js

方案一:用图片造get请求

1
2
3
4
5
6
7
8
9
10
button.addEventListener('click', (e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){ // 状态码是 200~299 则表示成功
alert('成功')
}
image.onload = function(){ // 状态码大于等于 400 则表示失败
alert('失败')
}
})

方案二:用script造get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script) // 和图片不同,必须添加到页面内才会执行
script.onload = function(e){ // 状态码是 200~299 则表示成功
// 删除script标签,如果想看见可以 debugger
e.currentTarget.remove()
}
script.onload = function(e){ // 状态码大于等于 400 则表示失败
alert('fail')
e.currentTarget.remove()
}
})
// 后端代码
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
response.setHeader('Content-Type', 'application/javascript')
response.write('amount.innerText = ' + amount) // 浏览器执行此代码以更新页面‘余额’
// 也可以使用es6:response.write(`amount.innerText = amount.innerText - 1`)
response.end()
}

这种技术叫做 SRJ - Server Rendered JavaScript

特点:域名无所谓

跨域 SRJ

JSONP

请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)

  1. 请求方创建 script,src 指向响应方,同时传一个查询参数 ?callbackName=yyy
  2. 响应方根据查询参数callbackName,构造形如
    • yyy.call(undefined, ‘你要的数据’)
    • yyy(‘你要的数据’)
      这样的响应
  3. 浏览器接收到响应,就会执行 yyy.call(undefined, ‘你要的数据’)
  4. 那么请求方就知道了他要的数据
    这就是 JSONP

方案3:JSONP

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
// 前端
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
let functionName = 'frank'+ parseInt(Math.random()*10000000 ,10)
window[functionName] = function(){ // 每次请求之前搞出一个随机的函数
amount.innerText = amount.innerText - 0 - 1
}
script.src = '/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function(e){ // 状态码是 200~299 则表示成功
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
script.onload = function(e){ // 状态码大于等于 400 则表示失败
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
})

// 后端
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
let callbackName = query.callback
response.setHeader('Content-Type', 'application/javascript')
response.write(`
${callbackName}.call(undefined, 'success')
`)
response.end()
}

约定

  1. callbackName 叫做 callback
  2. yyy 一般用 随机数 frank12312312312321325() // 因为可能同时调用很多网站jsonp,不如用过就没了
  3. 使用 jQuery来写
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $.ajax({ // 和ajax没关系
    url: "http://jack.com:8002/pay", // callback和函数名jq都会帮你写好
    dataType: "jsonp",
    success: function( response ) {
    if(response === 'success'){
    amount.innerText = amount.innerText - 1
    }
    }
    })

    $.jsonp()

面试题

请问 JSONP 为什么不支持 POST?
答:

  1. 因为 JSONP 是通过动态创建 script 实现的
  2. 动态创建 script 的时候只能用 get 不能用 post
  3. console.log(‘pathWithQuery’, ‘queryString’, ‘path’, ‘query’, ‘method’)
    console.log(pathWithQuery, queryString, path, query, method)

参考文章:阮一峰

A32-DOM 事件

Posted on 2018-03-05 | In 饥人谷

如何做「点击其他地方关闭浮层」

  • 防止冒泡
    w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true

  • 阻止默认行为
    w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//  html
<div id="wrapper" class="wrapper">
<button id="clickMe">点我</button>
<div id="popover" class="popover">
<input type="checkbox">浮层
</div>
</div>
// css
body{border: 1px solid red; }
.wrapper{position: relative; display: inline-block; }
.popover{border: 1px solid red; position: absolute; left: 100%;
top: 0; white-space: nowrap; padding: 10px; margin-left: 10px;
background: white; display: none; }
.popover::before{position: absolute; right: 100%; top: 5px;
border: 10px solid transparent; border-right-color: red;
content: ''; }
.popover::after{position: absolute; right: 100%; top: 5px;
border: 10px solid transparent; border-right-color: white;
content: ''; margin-right: -1px; }
// js
// 方案一: 给 document 添加事件,缺陷是如果有很多需要关闭的btn呢?监听事件会一直占着内存
clickMe.addEventListener('click', function(e){
popover.style.display = 'block'
})
wrapper.addEventListener('click', function(e){
e.stopPropagation() // 阻止冒泡,否则冒泡到docunment的监听事件时会执行
})
document.addEventListener('click', function(){
popover.style.display = 'none'
})
// 方案2:使用jQ,只有点击btn的时候才会对document进行监听,并且只执行一次,
$(clickMe).on('click', function() {
$(popover).show()
console.log('show')
setTimeout(function() { // 如果不添加setTimeout的话下面的docuemnt监听事件会执行,在执行clickMe的监听事件冒泡的时候,所以我们要“等一会”
console.log('添加 one click') // 这里添加了会使得冒泡结束后再执行setTimeout里面的代码
$(document).one('click', function() {
console.log('我觉得这里不会执行')
console.log('hide')
$(popover).hide()
})
}, 0)
})

// 理解DOM事件
<div class="red"> <div class="blue"> <div class="green">
<div class="yellow"> <div class="orange"> <div class="purple">
</div> </div> </div> </div> </div>
</div>

*{margin:0;padding:0;box-sizing:border-box;}
.red.active {background: red; }
.blue.active {background: blue; }
.green.active {background: green; }
.yellow.active {background: yellow; }
.orange.active {background: orange; }
.purple.active {background: purple; }
div {border: 1px solid black; padding: 10px;
transition: all 0.5s; display: flex;
flex:1; border-radius: 50%; background: white; }
.red{width: 100vw; height: 100vw; }

let divs = $('div')
let n = 0
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.add('active')
}, n * 500)
n += 1
}, true)
}
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.remove('active')
}, n * 500)
n += 1
})
}

新 jQuery API

  • .prepend()
  • .append()

A31-期中考试(部分)

Posted on 2018-02-28 | In 饥人谷

call、apply、bind

相同的地方

  1. 三者都是用来改变函数体内 this 对象的值
  2. 第一个参数都是 this 要指向的对象
  3. 可以接受更多参数(即第二个开始)来对函数传参
    不同的地方
  4. call 与 apply
    两者的却别仅在于接受后续参数方式的不同
    call第一个参数是this值没有变化,其余参数都是直接传递,也就是全部列举出来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function sum(x,y){
    if(x&&y){
    return (this.a + this.b + x + y)
    } else{
    return (this.a + this.b)
    }
    }
    var obj1 = {
    a: 1,
    b: 2
    }
    var obj2 = {
    c: 3,
    d: 4
    }
    console.log(sum.call(obj1)) // 3
    console.log(sum.call(obj1, obj2.c, obj2.d)) // 10

    apply的第一个参数为this值,第二个参数可以是 Array 的实例,也可以使 arguments 对象,也就是伪数组

    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
    function sum(x,y){
    if(x&&y){
    return (this.a + this.b + x + y)
    } else{
    return (this.a + this.b)
    }
    }
    var obj1 = {
    a: 1,
    b: 2
    }
    var obj3 = { // 伪数组
    0: 3,
    1: 4,
    length:2
    }
    console.log(sum.apply(obj1)) // 3
    console.log(sum.apply(obj1, obj3)) // 10
    console.log(sum.apply(obj1, [3,4])) // 10 传入数组
    // 传入 arguments 对象
    function sumall(a,b){
    this.a = 1
    this.b = 2
    return sum.apply(this, arguments)
    }
    console.log(sumall(3,4))
  5. bind 与前两者不同的返回值
    函数.call() 和 函数.apply() 都是对函数的直接调用
    而bind不同,它返回指定了this值的函数
    它也可以像call那样传参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function sum(x,y){
    if(x&&y){
    return (this.a + this.b + x + y)
    } else{
    return (this.a + this.b)
    }
    }
    var obj1 = {
    a: 1,
    b: 2
    }
    var obj2 = {
    c: 3,
    d: 4
    }
    var sum2 = sum.bind(obj1)
    console.log(sum2()) // 3
    console.log(sum2(obj2.c, obj2.d)) // 10

    CSS-移动端页面(响应式)

媒体查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 样式表中的CSS媒体查询
<style>
@media (min-width: 769px){
body{ background-color: purple;}
}
@media (min-width: 321px) and (max-width: 768px){
body{ background-color: orange;}
}
@media (max-width: 320px){
body{ background-color: red;}
}
</style>
// link元素中的CSS媒体查询
<link rel="stylesheet" media="(max-width: 800px)" href="example.css" />

动态 REM

rem

  1. 这个单位代表根元素的 font-size 大小(例如 元素的font-size)。
  2. rem 与 em 的区别
    首先理解em的意义,它表示当前元素的font-size的计算值,即em==font-size
    两者区别:rem 是相对于根元素的 font-size,而 em 是相对于当前元素的font-size
  3. 手机端方案的特点
    • 所有手机显示的界面都是一样的,只是大小不同
    • 1 rem == html font-size == viewport width

      一切单位以宽度为基准,就能完美还原设计稿
  4. 使用 js 动态调整 rem

    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
    // html
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script>
    // 1 rem 等于一个页面的宽度
    var pageWidth = window.innerWidth
    document.write('<style>html{font-size: '+pageWidth+'px;}</style>')
    </script>
    </head>
    <body>
    <div class="parent clearfix">
    <div class="child">1</div>
    <div class="child">2</div>
    <div class="child">3</div>
    <div class="child">4</div>
    </div>
    </body>
    </html>
    // css
    *{margin:0;padding:0;}
    body{
    // 设置字体大小,不然就变成和页面一样大了
    font-size: 16px;
    }
    .child{
    background-color: #ddd;
    float: left;
    }
    .clearfix::after{
    content: '';
    display: block;
    clear: both;
    }
    // 所有宽高边距等都用 rem 设置
    .child{
    width: 0.4rem;
    height: 0.2rem;
    margin: 0.05rem;
    }

    这样,不论手机宽度如何,比例仍然是一样的,只是大小不同
    另外,直接将rem与页面宽度等同,太大了,设置css都要使用小数,很麻烦
    所以可以设置的小一点,比如百分之一,但是这样可能会出现一个新的问题

    1
    2
    var pageWidth = window.innerWidth
    document.write('<style>html{font-size: '+ pageWidth/100 +'px;}</style>')

    得到的font-size值太小,别忘了chrome默认限制font-size最小为12px,于是页面就变‘大了’
    为了防止这样的情况,不能除以太大的数字

    1
    2
    var pageWidth = window.innerWidth
    document.write('<style>html{font-size: '+ pageWidth/10 +'px;}</style>')

    一般而言,10比较合适,因为智能手机屏幕通常在300-400px左右,除以10后在30-40,不会出现因为‘12px原则’而被‘放大页面’

  5. rem 可以与其他单位同时存在

    1
    2
    3
    font-size: 16px;
    border: 1px solid red;
    width: 0.5rem;

    比如 font-size,如果使用rem,会在屏幕很小的时候变得很难看
    还有border,1px转换rem在屏幕太小的时候仍然是1px,因为浏览器最小就是1px了
    这里建议太小的单位,或者如font-size这样的(字体太大太小都不好看),可以使用其它单位与rem混合使用
    比如px、em

  6. 在 SCSS 里使用 PX2REM
    这个是node-sass,
    npm config set registry https://registry.npm.taobao.org/
    touch ~/.bashrc
    echo ‘export SASS_BINARY_SITE=”https://npm.taobao.org/mirrors/node-sass“‘ >> ~/.bashrc
    source ~/.bashrc
    npm i -g node-sass
    mkdir ~/Desktop/scss-demo
    cd ~/Desktop/scss-demo
    mkdir scss css
    touch scss/style.scss
    start scss/style.scss
    node-sass -wr scss -o css

    编辑 scss 文件就会自动得到 css 文件

    在 scss 文件里添加

    @function px( $px ){
    @return $px/$designWidth*10 + rem;
    }

    $designWidth : 640; // 640 是设计稿的宽度,你要根据设计稿的宽度填写。如果设计师的设计稿宽度不统一,就杀死设计师,换个新的。

    .child{
    width: px(320);
    height: px(160);
    margin: px(40) px(40);
    border: 1px solid red;
    float: left;
    font-size: 1.2em;
    }
    即可实现 px 自动变 rem
    饥人谷教学视频

一些知识点

  • 如果不给页面添加font-size,则默认字体大小为16px
  • Chrome有最小字号设置,一般默认为12px,这个时候即使你设置更小的像素也无法生效,依然是12px(仅chrome)

闭包

  1. 什么是闭包
    闭包就是 函数和函数访问的函数外部变量的总和,
    1
    2
    3
    4
    var num = 100
    function addNum(){
    num += 1
    }

在上面,变量num+函数addNum就是闭包
实际上我们经常在用的东西

  1. 为什么要用闭包
    有时候我们需要隐藏变量,而不是直接访问他,来避免外部污染,
    通过函数来模拟块,在函数内部建立一个子函数来访问隐藏的变量,
    return这个子函数给外部的变量进行调用,来对隐藏变量进行操作,
    也因此,隐藏变量无法被垃圾回收销毁,在ie中会因为bug而出现内存泄漏
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function test(){
    var num = 1
    return function setNum(n){
    num = n
    return num
    }
    }
    var sn = test()
    console.log( sn(1) )
    console.log( sn(2) )
    console.log( sn(3) )

请说出至少三种排序的思路,这三种排序的时间复杂度分别为

O(n*n)
O(n log2 n)
O(n + max)

  1. O(n*n)
    冒泡排序:比较相邻的元素,如果第一个比第二个大,就交换他们两个,对每一对相邻的元素做同样的工作,从开始第一对到结尾最后一对,这步会让后一位元素就是最大的数,对所有元素重复以上步骤,除了最后一个,直到没有任何一堆数字需要比较
  2. O(n log2 n)
    快速排序:以一个元素为基准,重新排序数列,比基准值小的元素放左边,大的放右边,然后在对左半边和右半边重复以上操作,直到只有一个数字为止
  3. O(n + max)
    基数排序:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

著名前端面试题:

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

  1. DNS解析

    DNS解析的过程就是寻找哪台机器上有你需要资源的过程。当你在浏览器地址栏输入一个地址比如:www.baidu.com,其实并不是百度真正的地址,这只是方便人们记忆和理解所设计的,真正的地址是IP地址,而www.baidu.com是域名(ip地址和域名并不是一一对应的),DNS解析就是将你输入的域名解析成ip地址。

    查找(解析)过程:搜索浏览器自己的DNS缓存 –> 搜索操作系统中的DNS缓存 –> 搜索操作系统的hosts文件 –> 发送域名到本地域名服务器查询 –> 向根域名服务器查询 –> 向顶级域名服务器查询,

    得到ip地址,返回给浏览器,同时浏览器将ip地址缓存
  2. TCP连接
    得到服务器的ip地址后,接下来开始连接,总的来说需要经历以下三个过程(简称三次握手):
    1. 主机想服务器发送一个建立连接的请求(您好,我想认识您);
    2. 服务器接到请求后发送同意连接的信号(好的,很高兴认识您);
    3. 主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),自此,主机与服务器建立了连接
  3. 发送HTTP请求
    浏览器根据URL内容生成HTTP请求,HTTP报文被包裹在TCP报文中发送 ,服务器收到TCP报文是会解包提取出HTTP报文,它主要由三部分组成
    1. 请求行:请求方法 资源路径 HTTP协议版本
    2. 请求报头:请求报头允许客户端向服务器传递请求的附加信息和客户端自身的信息。
    3. 请求正文
  4. 服务器处理请求并返回HTTP报文
    HTTP响应报文也是由三部分组成: 状态码, 响应报头和响应报文。
  5. 浏览器解析渲染页面
    浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。
  6. 连接结束
    1. 主机向服务器发送一个断开连接的请求(不早了,我该走了);
    2. 服务器接到请求后发送确认收到请求的信号(知道了);
    3. 服务器向主机发送断开通知(我也该走了);
    4. 主机接到断开通知后断开连接并反馈一个确认信号(嗯,好的),服务器收到确认信号后断开连接;

如何实现数组去重?

假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。

要求:

不要做多重循环,只能遍历一次
请给出两种方案,一种能在 ES 5 环境中运行,一种能在 ES 6 环境中运行(提示 ES 6 环境多了一个 Set 对象)

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//  ES5:
var array = [1,5,2,3,4,2,3,1,3,4]
function unique(arr){
var tempArr = []
var hash = {}
for(var i=0; i<arr.length; i++){
if(hash[arr[i]] === undefined){
tempArr.push(arr[i])
hash[arr[i]] = 1
}
}
return tempArr
}
console.log(unique(array))

// ES6:
var arr = [1,5,2,3,4,2,3,1,3,4]
function unique(array) {
return [...new Set(array)]
}
console.log(unique(arr))

A30-无缝轮播

Posted on 2018-02-06 | In 饥人谷

任务代码

预览

用到的新 API

  1. .on('transitionend') -link
  2. 更多事件见-事件类型一览表
  3. .one() 为 元素的事件添加处理函数。处理函数在每个元素上每种事件类型最多执行一次。 -link
  4. ES6-模板字符串-表达式插补(${expression})

DOM事件模型

W3C
DOM Level 0: 在 W3C 出来之前的事实规范,比如 onclick,onmouseenter……或者直接在html里面写js函数
DOM Level 1: W3C整合之前的DOM0并没有做任何变更
DOM Level 2: 提供了 addEventListener 和 removeEventListener 等函数,这两个函数和onclick不同,onclick是属性,写第二个就会覆盖上一个,而addEventListener你可以认为是一个事件队列,一个事件一个队列,你可以往队列里添加多个函数,他们会按照顺序依次执行,removeEventListener则是将函数从队列中删除
同时,DOM2添加了两个阶段,捕获和冒泡

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
<div id="grand">
grand
<div id="parent">
parent
<div id="child">child</div>
</div>
</div>
// 先捕获,后冒泡
// 第三个参数默认为false,即冒泡阶段执行函数
grand.addEventListener('click', function(){console.log(grand)})
parent.addEventListener('click', function(){console.log(parent)})
child.addEventListener('click', function(){console.log(child)})
// 点击child div
// child parent grand

// 第三个参数为 true,即捕获阶段执行函数
grand.addEventListener('click', function(){console.log(grand)}, true)
parent.addEventListener('click', function(){console.log(parent)}, true)
child.addEventListener('click', function(){console.log(child)}, true)
// 点击child div
// grand parent child

// 如果是同一个div的话
child.addEventListener('click', function(){console.log(child1)}, false)
child.addEventListener('click', function(){console.log(child2)}, true)
// 点击
// child1 dhild2
// 同一个不管第三个参数,他们按js代码书写顺序执行

A29-用jQuery做个轮播

Posted on 2018-01-27 | In 饥人谷

这次我们将用jQuery做一个轮播

在遵循原则:内容(HTML)、样式(CSS)、行为(JavaScript)分离的情况下

面试套路

如果被问到你对于前端内容、样式、行为分离的理解。
我们当然认为这是理所当然的,但是这么回答显然不行。
所以,我们可以反着回答,如果不分离的话会有什么坏处,以此论证需要分离。
例如:

  • HTML负责样式

    1
    2
    3
    4
    5
    <body bgcolor="gray">
    <center>
    <font color="red" size="20">你好</font>
    <center>
    </body>

    现在,有的标签是为了表示样式的,有的标签是为了表示内容的,这使得我们难以区分这些标签真正的逻辑结构

  • CSS 表示内容

    1
    2
    3
    4
    5
    6
    // html
    <div id="x"></div>
    // css
    div::after{ content: '你好'; }
    // js
    console.log(x.innerText) // 什么都没有,空的

    使用css表示内容,会导致用户无法用鼠标选中它,同时,js也无法获取它的内容

  • CSS 表示行为
    使用css表示复杂的行为,会导致页面变得很慢很慢
    优化的原则之一:不要使用IE发明的CSS Expressions
  • JS 表示 CSS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // html
    <div id="x"></div>
    // js
    var $div = $('#x')
    $div.hide() // 影藏很好理解,display:none
    $div.show() // 显示呢?block?可如果我本来是flex呢?或者inline-flex呢?
    // jQuery 为了处理这个问题,在 hide() 时获取了你的 display 值
    // 使得 flex 在经过 hide() show()之后依然是 flex
    // 可是,如果本来是 none 呢?
    // css
    div{ display:none; }
    // 测试结果显示 show() 之后是 block,也就是说 show() 默认 block

    这种结果显然不符合我们的预期,这里,建议使用添加 class 的方式来控制,js 只负责行为,它不知道一个div应该怎么show,block?flex?
    显示还是不显示?那是 css 的活。
    $div.addClass('active')

思路

  1. 将图片全部放到一个div里,把图片放平,图片最好是一样大小的
  2. 然后用一个窗口来看图片,图片从左到右挨个出现,就好像以前的电影胶带
  3. 有了思路,剩下的就是实现和改bug,这次是用jq来写,会出现很多不认识的api,直接查文档就行了

关于封装

  1. 从 API 开始思考
  2. 尽量能让人看见就明白是干嘛的

成果

  • 预览
  • 代码

本次任务用到的 jQuery API

  1. .on() -link
  2. index() -link
  3. .eq() -link
  4. .trigger() -link
  5. .addClass() -link
  6. .removeClass() -link
  7. .siblings() -link
  8. .css() -link

优化原则

  1. 不使用 CSS 来表示复杂的行为,不要使用IE发明的CSS Expressions。
  2. 如果你已经知道图片的宽高了,那么最好是写在上面<img width=xx height=xx></img>,这样浏览器就会少一次让后面的图片或者其它元素让位的过程。

奇葩bug

  1. css tansform: translateX();在浏览器放大页面下出现bug,抖动

A28-JQuery

Posted on 2018-01-26 | In 饥人谷

这次我们自己实现一个类似Query的API(简化版)

封装函数

获取一个元素所有兄弟元素

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
// html
<ul>
<li id="item1">选项1</li>
<li id="item2">选项2</li>
<li id="item3">选项3</li>
<li id="item4">选项4</li>
<li id="item5">选项5</li>
</ul>

// js, 假设我们获取 item3 的所有兄弟元素
var allChildren = item3.parentNode.children
var array = {
length: 0;
}
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== item3){
array[array.length] = allChildren[i]
array.length += 1
}
}
console.log(array) // 测试
// 这样就获得 item3的所有兄弟元素了
// 接下来我们将这段代码封装起来
function getSiblings(node){ /* API */
var allChildren = node.parentNode.children
var array = {
length: 0;
}
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array // 返回一个伪数组
}
console.log(getSiblings(item2)) // test
// 这样,一个api就做好了,它可以获得一个元素的所有兄弟元素

接下来我们开始给一个元素添加 class

仍然是上面那段 html

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
// 为元素添加多个 class
item3.classList.add('a')
item3.classList.add('b')
item3.classList.add('c')

// 简洁一点的写法
var classes = ['a','b','c']
classes.forEach( value => item3.classList.add(value) )

// 这次我们既可以 add 也可以 remove
var classes = {'a':true, 'b':false, 'c':true}
for(let key in classes){
if(classes[key]){
item3.classList.add(key)
}
else{
item3.classList.remove(key)
}
}

// 封装成函数
function addClass(node, classes){
for(let key in classes){
/*
if(classes[key]){
node.classList.add(key)
}
else{
node.classList.remove(key)
}
*/
// 代码优化守则1:如果出现类似的代码,就存在优化的可能
// 优化这段代码
let methodName = class[key] ? 'add' : 'remove'
node.classList[methodName](key)
}
}
addClass(item3, {a:true,b:false,c:true}) // test

命名空间

上面两个函数是有关联性的,他们都是对node进行操作
现在我们来声明一个变量,将它们放进去

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
window.zdom = {}

zdom.getSiblings = function(node){
var allChildren = node.parentNode.children
var array = {
length: 0;
}
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array // 返回一个伪数组
}

zdom.addClass = function(node, classes){
for(let key in classes){
let methodName = class[key] ? 'add' : 'remove'
node.classList[methodName](key)
}
}

zdom.getSiblings(item3)
zdom.addClass(item3,{a: true, b: true, c: flase})

有一个库就是这样的方式:yui
这就叫做命名空间,也是一种设计模式。所有的套路都是设计模式,就比如哈希,就比如数组。
这种常用的模式或者组合就叫做设计模式。
为什么要命名空间:想想JQuery,那么多api,难道对别人说的时候要一个一个报出来么,所以一个统称的名字就很必要了,同时,你怎么知道别人没写addClass呢?全部放到window里不会产生覆盖么,所以,
如果没有命名空间:

  1. 别人不知道你的库叫什么
  2. 你会不知不觉把所有全局变量都覆盖了

将 node 放在前面

相较于命名空间的调用方式(这种方法已经过时了)
zdom.getSiblings(item3)
zdom.addClass(item3,{a: true, b: true, c: flase})
我们认为这样的方式更好:
item3.getSiblings()
item3.addClass({a: true, b: true, c: flase})

这时候,就需要用到原型,来扩展 Node 接口,
这是第一种方法

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
Node.prototype.getSiblings = function(){
var allChildren = this.parentNode.children
var array = {
length: 0;
}
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== this){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array // 返回一个伪数组
}
Node.prototype.addClass = function(classes){
for(let key in classes){
let methodName = class[key] ? 'add' : 'remove'
this.classList[methodName](key)
}
}
// 隐式指定 this
item3.getSiblings()
item3.addClass({a: true, b: true, c: flase})
// 显式指定 this
item3.getSiblings.call(item3)
item3.addClass.call(item3, {a: true, b: true, c: flase})

但是改写Node.prototype可能会出现覆盖情况,所以又有了第二种方法:
这种叫做「无侵入」

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
window.Node2 = function(node){
return {
getSiblings: function(){
var allChildren = node.parentNode.children
var array = {
length: 0;
}
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array // 返回一个伪数组
},
addClass: function(classes){
for(let key in classes){
let methodName = class[key] ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}
var node2 = Node2(item3)
node2.getSiblings()
node2.addClass({a: true, b: true, c: flase})

给 Node2 换个名字 jQuery

1
2
3
4
5
6
7
8
9
window.jQuery = function(node){
return {
getSiblings: function(){ ... },
addClass: function(classes){ ... }
}
}
var node2 = jQuery(item3)
node2.getSiblings()
node2.addClass({a: true, b: true, c: flase})

除了名字换了一下,和刚才的代码并没有区别
只是Node2变成了jQuery
jQuery就是一个这么升级了的DOM
它接受一个旧的节点,然后返回你一个新的对象
这个新的对象有新的API,它的内部实现依然是去调用旧的API,只是变得更好用、更方便(一句话相当于以前十句话)
然而jQuery的厉害不止于此,它还可以接受选择器

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
window.jQuery = function(nodeOrSelector){
let node
// 类型检测
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
} else{
node = nodeOrSelector
}
return {
getSiblings: function(){
var allChildren = node.parentNode.children
var array = { length: 0; }
for(let i=0; i < allChildren.length; i++){
if(allChildren[i] !== node){ // 注意这里用到了闭包,匿名函数访问了外面的 node
array[array.length] = allChildren[i] // node 和 匿名函数组成了闭包(下面的函数同理)
array.length += 1
}
}
return array // 返回一个伪数组
},
addClass: function(classes){
for(let key in classes){
let methodName = class[key] ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}
var node2 = jQuery('#item3')
// var node2 = jQuery('ul > li:nth-child(3)')
node2.addClass({a: true, b: true, c: flase})

那么如果要操作多个节点呢?

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
window.jQuery = function(nodeOrSelector){
let nodes = {}
if(typeof nodeOrSelector === 'string'){
let temp = document.querySelectorAll(nodeOrSelector) // 伪数组
for(let i=0; i < temp.length; i++){ // 如果你想要一个纯净的伪数组不要那些其它属性方法
nodes[i] = temp[i]
}
node.length = temp.length
} else if(nodeOrSelector instanceof Node) {
nodes = {0: nodeOrSelector, length: 1}
}
nodes.getSiblings = function(){/*太麻烦不做了*/}
nodes.addClass = function(classes){
classes.forEach((value) => {
for(let i=0; i < nodes.length; i++){
nodes[i].classList.add(value)
}
})
}
// 添加几个有用的 jQuery Api
nodes.getText = function(){
let texts = []
for(let i=0; i < nodes.length; i++){
texts.push(nodes[i].textContent)
}
return texts
}
nodes.setText = function(text){
for(let i=0; i < nodes.length; i++){
nodes[i].textContent = text
}
}
// 然而实际上 jQuery 合并了 get和set
// 类似的 api jquery 有很多
nodes.text = function(text){
// jQuery 认为,你没有设置参数,那就是要获取 text,
// 如果有参数,那就是要设置 text
if(text === undefined){
let texts = []
for(let i=0; i < nodes.length; i++){
texts.push(nodes[i].textContent)
}
return texts
} else{
for(let i=0; i < nodes.length; i++){
nodes[i].textContent = text
}
}
}
return nodes
}

var node2 = jQuery('ul > li')
node2.addClass(['red'])
var text = node2.text()
node2.text('hi')

缩写 alias

1
2
3
window.$ = jQuery
// 建议使用 jQuery 构造出来的 对象都在前面加一个 $ ,以表示它是由$构造的,避免和正常的弄混
var $node2 = $('ul>li')

知道了 jQuery 是怎么实现的,现在去看看真正的 jQuery吧 -MDN

  • jQuery Api
  • jQuery Api中文网
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // html
    <ul>
    <li class='red'>选项1</li>
    <li class='red'>选项2</li>
    <li class='red'>选项3</li>
    <li class='red'>选项4</li>
    <li class='red'>选项5</li>
    </ul>
    <button id=x></button>

    // css
    .red{color:red;}
    .green{color:green;}

    // js,使用 jQuery
    var nodes = jQuery('ul>li')
    var classes = ['red','green','blue','yellow','black']
    x.onclick = function(){
    /* 常规写法
    nodes.removeClass('red')
    nodes.addClass('green')
    */
    nodes.removeClass('red').addClass('green') // 链式操作
    }

总结

现在大概已经知道 jQuery 是怎么写的了,明白了它的原理,接下来只需要多了解它的api,
但实际上 jQuery 并没有这么简单,作为曾经风靡前端的库,它的强大超乎我们的想象。

  • jQuery 在兼容性方面做得很好,1.7 版本兼容到 IE 6
  • jQuery 还有动画、AJAX 等模块,不止 DOM 操作
  • jQuery 的功能更丰富
  • jQuery 使用了 prototype,我们没有使用,等学了 new 之后再用

库是特定种类的API

A27-DOM API

Posted on 2018-01-25 | In 饥人谷

DOM

  • MDN
  • 阮一峰
    什么是DOM,DOM最重要的就是要明白它的概念,否则遇到问题也不知道如何下手
    大家都说DOM树,这是一个比喻,易于理解,
    D就是文档,想象它是一个树状的结构(就像之前数据结构里的二叉树那样),树上有着节点(node)
    O就是对象,
    M也就是模型,把D和O做一个一一对应的映射,就是这个模型
    所以DOM就是把文档变成一个对象
    映射
    上图红字
    DOM转化来的js对象里面到底存放什么由DOM标准规定
    比如:
    上图的 head、body、meta、link、h1、p 都是Element的实例
    (中间存在其它函数,F12调试可见,比如document.body就是document.body.proto->HTMLBodyElement.prototype.proto->HTMLElement.prototype.proto->Element.prototype)
    根节点html比较特殊,是由document构造的(DOucment->HTMLDocument,简写)
    至于文本节点则是由

Node Api

Node 属性

记住以下单词;

  • child / children / parent
  • node
  • first / last
  • next / previous
  • sibling / siblings
  • type
  • value / text / content
  • inner / outer
  • element
    然后相互组合
    一部分:childNodes,firstChild,innerText,lastChild,nextSibling,nodeName,nodeType,nodeValue,outerText,ownerDocument,parentElement,parentNode,previousSibling,textContent
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <html>
    <head></head>
    <body></body>
    </html>

    document.body.previousSibling // #text
    document.body.previousSibling.previousSibling // <head></head>
    docuemnt.body.previousElementSibling // <head></head>
    // 为什么previousSibling返回的是文本节点呢,这是因为本来dom是配对xml的,后来强行配对html
    // previousSibling是 Node 的属性,previousElementSibling是 Element 的属性
    // previousSibling 是本来就有的,previousElementSibling是后来加的
    // 类似的还有 nextSibling / nextElementSibling, firstChild / firstElementChild 等等
    // 可以在F12中尝试,控制台会显示提示以及该属性属于哪个对象

几个要注意的 Node Api

  • nodeName
    注意这个api很奇葩
    document.body.nodeName // 'BODY
    document.documentElement.nodeName // 'HTML
    document.nodeName不行,必须像上面那样写
    它对所有标签返回名称都是大写
    但是!唯有svg,它返回小写
    document.getElementsByTagName('svg')[0] // 'svg'
    这是因为svg是‘外来的’标签,它是后来新加的,然后就这么小写了……
  • nodeType -MDN
    只读属性 Node.nodeType 表示的是该节点的类型。其所有可能的值请参考节点类型常量.
    它返回一个整数来表示节点的类型
    (之所以这样是因为当年的计算机还没这么发达,而1比element的字节小多了)
    以下几个需知:
    1 元素节点, Eg:<div>、<p>
    3 文本节点, 元素或者属性中实际的文字,比如<p>段落</p>中的段落二字
    9 Document节点,
    11 DocumentFragment节点 // 很特殊的一个节点(暂时我还不清楚,google DocumentFragment优化)

    1
    2
    3
    4
    5
    document.body.nodeType // 1
    document.nodeType // 9
    document.documentElement.nodetype // 1
    document.documentElement.nodeName // 'HTML'
    // 所以document.documentElement才是html,document不是?
  • innerText与textContent -MDN
    区别:

    • textContent 会获取所有元素的内容,包括 <script> 和 <style> 元素,然而 innerText 不会。
    • innerText意识到样式,并且不会返回隐藏元素的文本(设置display:none的元素),而textContent会。
    • 由于 innerText 受 CSS 样式的影响,它会触发重排(reflow),但textContent 不会。
    • 与 textContent 不同的是, 在 Internet Explorer (对于小于等于 IE11 的版本) 中对 innerText 进行修改, 不仅会移除当前元素的子节点,而且还会永久性地破坏所有后代文本节点(所以不可能再次将节点再次插入到任何其他元素或同一元素中)。
  • childNodes -MDN
    Node.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合(live collection)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var parent = document.getElementById('parent');
    parent.childNodes.length // 2
    parent.appendChild(document.createElement('div'));
    parent.childNodes.length // 请问现在 length 是多少

    // 3

    // 与之相反的是 querySelectorAll() ,它返回的是一个静态的 NodeList
    var allDiv = document.querySelectorAll('div>
    allDiv.length // 假设是 2
    document.body.appendChild( document.createElement('div') )
    allDiv.length // 请问现在 length 的值是多少???
    // 2
    // querySelectorAll方法不属于 node,是document 和 element 的
  • HTMLCollection与NodeList的区别有

    • HTMLCollection实例对象的成员只能是Element节点,NodeList实例对象的成员可以包含其他节点。
    • HTMLCollection实例对象可以用id属性或name属性引用节点元素,NodeList只能使用数字索引引用。
    • HTML DOM 中的 HTMLCollection 是即时更新的(live);
      当其所包含的文档结构发生改变时,它会自动更新。
      NodeList 对象大多数情况下是个实时集合。
      意思是说,如果文档中的节点树发生变化,则已经存在的 NodeList 对象也可能会变化。例如,Node.childNodes 是实时的。
      在另一些情况下,NodeList 是一个静态集合,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容。document.querySelectorAll 返回一个静态的 NodeList。

      Node 方法 (如果一个属性是函数,那么这个属性就也叫做方法;换言之,方法是函数属性)

  • appendChild()
  • cloneNode() // 分深、浅拷贝
  • contains()
  • hasChildNodes()
  • insertBefore()
  • isEqualNode() // 看起来相等
  • isSameNode() // 完全相等,可以用 === 来代替
  • removeChild()
  • replaceChild()
  • normalize() // 常规化
    基本看见名字就知道作用,不清楚也可以查MDN。

Document Api -MDN

Document 属性

  • body
  • characterSet
  • childElementCount
  • children
  • doctype
  • documentElement
  • domain
  • fullscreen
  • head
  • hidden
  • images
  • links
  • location
  • onxxxxxxxxx
  • origin
  • plugins
  • readyState
  • referrer
  • scripts
  • scrollingElement
  • styleSheets
  • title
  • visibilityState

    Document 方法:

  • close()
  • createDocumentFragment()
  • createElement()
  • createTextNode()
  • execCommand()
  • exitFullscreen()
  • getElementById()
  • getElementsByClassName()
  • getElementsByName()
  • getElementsByTagName()
  • getSelection()
  • hasFocus()
  • open()
  • querySelector()
  • querySelectorAll()
  • registerElement()
  • write()
  • writeln()

Element Api -MDN

关于 DOM API 更多见之后写的常用 API。(待续)

A26-JS函数

Posted on 2018-01-24 | In 饥人谷

Function -MDN

1
2
3
var f = function(a,b){
return a+b
}

语法:new Function ([arg1[, arg2[, ...argN]],] functionBody)
参数:
arg1, arg2, ... argN
被函数使用的参数的名称必须是合法命名的。参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表;例如“×”,“theValue”,或“A,B”。
functionBody
一个含有包括函数定义的JavaScript语句的字符串

函数总是会返回一个值

1
2
3
4
function fn(){
console.log('函数')
return undefined // 如果你不写就会自动添加这样的一句
}

函数的5种声明方式

function 有一个属性:name

  1. 具名函数

    1
    2
    3
    4
    5
    function f(x,y){
    console.log(x+y) // 打印什么和返回什么没有必然联系
    return undefined // 必须有一个return,没有的话也会被自动添加本句
    }
    f.name // 'f'
  2. 匿名函数
    它不能单独使用,可以赋给一个变量来使用函数表达式

    1
    2
    3
    4
    5
    var f
    f = function(x,y){
    return x+y
    }
    f.name // 'f'
  3. 具名函数赋值 -阮一峰

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function f(){}
    f.name // 'f'
    console.log(f) // 'function f(){}'

    var f2 = function fn(){}
    f2.name // 'fn'
    console.log(fn) // Uncaught ReferenceError: fn is not defined
    // 只能在 function fn(){}这个函数里面访问到fn,在外面无效
    // js 的不一致性
  4. window.Function 函数对象

    1
    2
    3
    4
    5
    6
    7
    var f = new FUnction('x', 'y', 'return x+y')
    f.name // 'anonymous'

    var n=1
    var fn = new Function('x', 'y', 'return x+' + n + '+y' )
    console.log(fn) // f anonymous(x,y){ return x+1+y }
    fn(1,2) // 4
  5. 箭头函数 -MDN

    1
    2
    3
    4
    5
    var f = (x,y) => {return x+y}
    f.name // 'f'

    var f2 = (x,y) => x+y // 只有一句 return 的时候,可以将 return 和 {} 省略
    var f3 = n => n*n // 如果参数只有一个,() 可以省略

函数的本质

函数是一段可以反复调用的代码块。
函数还能接受输入的参数,不同的参数会返回不同的值。
函数和数据不同,不能直接使用,只能调用

1
2
3
4
5
6
7
8
9
10
11
12
13
// 定义一个 n
var n=2
// 然后直接使用它就好了
var m = n // 比如将它赋值给 m
console.log(n) // 或者打印出来

// 定义一个函数
function f(x,y){
return x+y
}
f // 打印出 f 函数,等于啥也没做,return x+y 并没有执行
// 函数只能调用(call)
f(1,2) // 3

函数是怎样储存的


函数也是对象的一种,在stack中储存地址,在heap中储存对象,保存的是字符串(就是你定义的函数,参数、函数体等),通过Function.prototype里的call()来调用函数体
我们来试着用对象来做一个这样的函数

1
2
3
4
5
6
7
8
9
10
11
12
var f = {}
f.name = 'f' // 函数本身就有的属性
f.params = ['x','y'] // 模拟参数
f.functionBody = 'console.log(1)' // 模拟函数体,不管参数还是函数体,都是字符串形式
f.call = function(){
return window.eval(f.functionBody)
}
f.call()
// 1
// undefined, console.log()的返回值
// 这就是为什么说函数是对象,函数调用实际上就是eval()函数的函数体的过程
// f与f.call()区别:f是变量,是函数(这里用的是对象),f.call()是调用函数

所以函数就是可以执行代码的对象
eval() -MDN
eval()函数会将传入的字符串当做JavaScript代码进行执行。

f(1,2) 与 f.call(undefined, 1, 2)


前者是语法糖,后者才是真正的写法
理解这两者的区别才能更好的理解this
SegmentFault

call()怎么用 -MDN

1
2
function f(x,y){ return x+y }
f.call(undefined, 1, 2) // 3

call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
语法:fun.call(thisArg, arg1, arg2, ...)
参数:
thisArg
在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,
如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),
同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
在严格模式下,this将保持他进入执行上下文时的值,所以下面的this将会默认为undefined。(详见this-mdn)
arg1, arg2, …
指定的参数列表。
返回值:返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。
描述:
可以让call()中的对象调用当前对象所拥有的function。
你可以使用call()来实现继承: 写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

this -MDN-阮一峰 与 arguments MDN

什么是this,在以下代码中
f.call(undefined, 1, 2)
undefined就是this
1和2就是arguments
arguments是伪数组别忘了
或者说:
call的第一个参数可以用this得到
call的后面所有参数可以用arguments得到

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
// Eg
var f = function(){
console.log(this)
console.log(this === window)
console.log(arguments)
}
f.call(undefined, 1,2,3)
// log this -> window 对象 原因见上文-call()怎么用
// true
// log arguments -> [1,2,3] 还有一些属性api见mdn

var f2 = function(){
'use strict' // use 严格模式
console.log(this)
console.log(this === window)
console.log(arguments)
}
f2.call(undefined)
// undefined
// false
// []

f2.call(7)
// 7 false []
f2.call('foo')
// 'foo' false []

var f3 = function() {
console.log(Object.prototype.toString.call(this));
}
//原始值 1 被隐式转换为对象
f3.call(1); // [object Number]

注意: 使用 call 和 apply 函数的时候,如果传递的 this 值不是一个对象,JavaScript 将尝试使用内部 ToObject 操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 ‘foo’,那么就会使用相关构造函数将它转换为对象,所以原始值 7 通过new Number(7)被转换为对象,而字符串’foo’使用 new String(‘foo’) 转化为对象。(这一点上文也有描述)
this 也是为了长得像 java 而诞生的,和 new 一样

call stack 调用栈

表示函数或子例程像堆积木一样存放,以实现层层调用。
别忘了栈是先入后出。
嵌套调用

  • 普通调用
  • 嵌套调用
  • 递归调用
    stack overflow 栈溢出
    call stack 超过电脑分配空间就会出现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function sum(n){
    if(n==1){
    return 1
    }else{
    return n + sum.call(undefined, n-1)
    }
    }

    sum.call(undefined,5) //15

    sum.call(undefined, 100000)
    // Uncaught RangeError: Maximum call stack size exceeded
    // 最大调用栈大小超过
  • call stack-mdn

  • call stack-阮一峰
  • stack overflow-wiki
  • stackoverflow.com捏他的同名程序问答网站
  • 国内版stackoverflow->SegmentFault

作用域 -MDN-阮一峰

  • 按照语法树,就近原则
  • 我们只能确定变量是哪个变量,但是不能确定变量的值

    面试题

    拿到代码直接做——必然会错。请先提升声明
    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
    // 1.
    var a = 1
    function f1(){
    alert(a) // 是多少
    var a = 2
    }
    f1.call() // undefined

    // 2.
    var a = 1
    function f1(){
    var a = 2
    f2.call()
    }
    function f2(){
    console.log(a) // 是多少
    }
    f1.call() // 1

    // 3.
    <ul>
    <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li>
    </ul>
    var liTags = document.querySelectorAll('li')
    for(var i = 0; i < liTags.length; i++){
    liTags[i].onclick = function(){
    console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
    }
    }
    // 打印 6
    // 当你点击时for早就遍历完,i 已经自增到 6

闭包 -MDN-方应杭

A25-JS数组

Posted on 2018-01-20 | In 饥人谷
  • Array -MDN

    window.Array 全局对象(也是函数)

    • 基本用法:

      1
      2
      3
      4
      5
      6
      7
      var a = Array(3)  // {length: 3}
      a.length // 3
      0 in a // false, 数组 a 里面并没有 0 这个 key
      a[0] // undefined
      a.push('three')
      3 in a // true
      a[3] // 'three'

      1
      2
      3
      4
      5
      6
      var a2 = Array(3,3) // [3,3]
      a2.length // 2
      0 in a2 // true
      a2[0] // 3
      // 当括号里只有一个数字参数的时候,它是长度,当参数不止一个的时候它是参数
      // 这叫做不一致性,这不是一件好事

      1
      2
      3
      4
      new Array(3) 跟不加 new 一样的效果
      new Array(3,3,) 跟不加 new 一样的效果

      var a = [1,2,3] // 常用简写方式
    • 加不加new对不同类型的区别:

      • String、Number、Boolean
        String()返回基本类型
        new String()返回对象
      • Object、Array、Function
        Object()返回对象
        new Object()返回依然是对象
    • 数组为什么是数组而不是对象是因为数组有着对象没有的特点
      也就是你的对象的__proto__指向的是否是Array.prototype
    • 数组的一个特征
      数组之所以是数组,其实是因为你把它数组

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      var a = [1,2,3]
      a.x = 'x'
      a.y = 'y'
      for(let i=0; i < a.length; i++){ // 这种方法遍历排除了 x、y
      console.log(a[i]) // 1, 2, 3
      }
      for(let key in a){
      console.log(key) // 0, 1, 2, x, y
      }
      var obj = {
      0: 1, 1: 2, 2: 3, length: 3
      }
      for(let i=0; i < obj.length; i++){
      console.log(obj[i]) // 1, 2, 3
      }
    • 伪数组
      上面的obj对象就是伪数组;它不关心你有没有数组的那些方法,只需要012345……这些下标去遍历
      只要你的原型链中没有Array.prototype就是伪数组
      目前JS中只有一种伪数组,就是arguments

      1
      2
      3
      4
      5
      6
      7
      8
        function f(){ console.dir(arguments) }
      f(1,2,3)
      ```
      从下图可以看见它引用的是`Object.protype`而非`Array.prototype`
      ![](http://upload-images.jianshu.io/upload_images/9047034-54e385e6ddc2d1bb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
      - `forEach`API -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)[阮一峰](http://javascript.ruanyifeng.com/stdlib/array.html#toc15)
      forEach() 方法对数组的每个元素执行一次提供的函数。
      语法:

      array.forEach(callback(currentValue, index, array){

      //do something
      

      }, this)

      array.forEach(callback[, thisArg])

      // Eg
      const arr = [‘a’, ‘b’, ‘c’];

      arr.forEach(function(element) {

      console.log(element);
      

      });
      arr.forEach( element => console.log(element));
      // a
      // b
      // c

      // 上面我们并没有将arr传进forEach里面但是为什么还能调用
      // 可以这么理解
      // 用this来获取,比如
      var obj = { 0:’a’, 1:’b’, length:2 }
      obj.forEach = function(fn){
      for(let i=0; i < this.length; i++){

      fn(this[i], i, this)
      

      }
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
        参数:
      `callback`
      为数组中每个元素执行的函数,该函数接收三个参数:
      `currentValue`(当前值)
      数组中正在处理的当前元素。
      `index`(索引)
      数组中正在处理的当前元素的索引。
      `array`
      `forEach()`方法正在操作的数组。
      `thisArg`可选
      可选参数。当执行回调 函数时用作this的值(参考对象)。
      返回值:`undefined`.
      - `sort()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)[阮一峰](http://javascript.ruanyifeng.com/stdlib/array.html#toc13)
      sort方法对数组成员进行排序,默认排序顺序是根据字符串Unicode码点。 sort 排序不一定是稳定的排序后,原数组将被改变。

      [‘d’, ‘c’, ‘b’, ‘a’].sort()
      // [‘a’, ‘b’, ‘c’, ‘d’]

      [4, 3, 2, 1].sort()
      // [1, 2, 3, 4]

      var fruit = [‘cherries’, ‘apples’, ‘bananas’];
      fruit.sort();
      // [‘apples’, ‘bananas’, ‘cherries’]

      var scores = [1, 10, 21, 2];
      scores.sort();
      // [1, 10, 2, 21]
      // 注意10在2之前,
      // 因为在 Unicode 指针顺序中”10”在”2”之前

      var things = [‘word’, ‘Word’, ‘1 Word’, ‘2 Words’];
      things.sort();
      // [‘1 Word’, ‘2 Words’, ‘Word’, ‘word’]
      // 在Unicode中, 数字在大写字母之前,
      // 大写字母在小写字母之前.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      语法:
      `arr.sort()`
      `arr.sort(compareFunction)`
      参数:
      `compareFunction`
      可选。用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。
      返回值: 返回排序后的数组。原数组已经被排序后的数组代替。
      描述:
      如果没有指明 `compareFunction` ,那么元素会按照转换为的字符串的诸个字符的 Unicode 位点进行排序。例如 "Banana" 会被排列到 "cherry" 之前。当数字按由小到大排序时,9 出现在 80 之前,但因为(没有指明 `compareFunction`),比较的数字会先被转换为字符串,所以在Unicode顺序上 "80" 要比 "9" 要靠前。
      如果指明了 `compareFunction` ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
      + 如果 `compareFunction(a, b)` 小于 0 ,那么 a 会被排列到 b 之前;
      + 如果 `compareFunction(a, b)` 大于 0 , b 会被排列到 a 之前
      + 如果 `compareFunction(a, b)` 等于 0 , a 和 b 的相对位置不变
      + `compareFunction(a, b)` 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

      // 对数字排序
      var a = [5,6,4,1,2,3]
      // 升序
      function up(a, b) {
      if (a < b ) { // 按某种排序标准进行比较, a 小于 b

      return -1;
      

      }
      if (a > b ) {

      return 1;
      

      }
      // a must be equal to b
      return 0;
      }
      // 更简洁的写法
      function up(a,b){
      return a-b
      }
      a.sort(up) // 1,2,3,4,5,6

      // 降序
      function down(a,b){
      return b-a
      }

      1
      2
      3
      4
      5
        js内置的排序一般都是快排
      - `join()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/join)
      join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
      和sort()方法不同,它不会改变原有的数组
      语法:

      str = arr.join()
      // 默认为 “,”

      str = arr.join(“”)
      // 分隔符 === 空字符串 “”

      str = arr.join(separator)
      // 分隔符

      1
      2
      3
      4
      5
      6
      7
      8
      参数:
      `separator`
      指定一个字符串来分隔数组的每个元素。
      如果需要(`separator`),将分隔符转换为字符串。
      如果省略(),数组元素用逗号分隔。默认为 ","。
      如果separator是空字符串(""),则所有元素之间都没有任何字符。
      返回值
      一个所有数组元素连接的字符串。如果 arr.length 为0,则返回空字符串

      // Eg
      let a = [‘Wind’, ‘Rain’, ‘Fire’];

      console.log(a.join());
      // 默认为 “,”
      // ‘Wind,Rain,Fire’

      console.log(a.join(“”));
      // 分隔符 === 空字符串 “”
      // “WindRainFire”

      console.log(a.join(“-“));
      // 分隔符 “-“
      // ‘Wind-Rain-Fire’

      1
      2
      3
      4
      5
      6
      7
      - `concat()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)
      concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
      语法:
      `var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])`
      参数:valueN
      将数组和/或值连接成新数组。
      返回值:新的 Array 实例。

      var arr1 = [‘a’, ‘b’, ‘c’];
      var arr2 = [‘d’, ‘e’, ‘f’];

      var arr3 = arr1.concat(arr2);

      // arr3 is a new array [ “a”, “b”, “c”, “d”, “e”, “f” ]

      // 使用 concat 来复制一个数组
      var a = [1,2,3]
      bar b = a.concat([])
      a === b // false

      1
      2
      3
      4
      - `map()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map)[阮一峰](http://javascript.ruanyifeng.com/stdlib/array.html#toc14)
      map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
      map与forEach类似,不同的是它返回一个新数组,而forEach返回undefined。
      语法:

      let new_array = arr.map(function callback(currentValue, index, array) {

      // Return element for new_array 
      

      }[, thisArg])

      // Eg
      var numbers = [1, 2, 3];

      numbers.map(function (n) {
      return n + 1;
      });
      // [2, 3, 4]

      numbers
      // [1, 2, 3]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
        参数:
      `callback`
      生成新数组元素的函数,使用三个参数:
      `currentValue`
      callback 的第一个参数,数组中正在处理的当前元素。
      `index`
      callback 的第二个参数,数组中正在处理的当前元素的索引。
      `array`
      callback 的第三个参数,map 方法被调用的数组。
      `thisArg`
      可选的。执行 callback 函数时 使用的this 值。
      返回值:
      一个新数组,每个元素都是回调函数的结果。
      - `filter()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
      filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
      语法:
      `var new_array = arr.filter(callback[, thisArg])`
      返回值: 一个新的通过测试的元素的集合的数组
      ES6:
      let [...spread]= [12, 5, 8, 130, 44];
      等同于:let spread = 浅克隆([12, 5, 8, 130, 44])

      function isBigEnough(value) {
      return value >= 10;
      }

      var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);

      // filtered is [12, 130, 44]

      // ES6 way

      const isBigEnough = value => value >= 10; // 这里用的箭头函数

      let […spread]= [12, 5, 8, 130, 44];

      let filtered = spread.filter(isBigEnough);

      // filtered is [12, 130, 44]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      - `reduce()` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
      reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
      语法:`arr.reduce(callback(accumulator,currentValue)[, initialValue])`
      参数
      `callback`
      执行数组中每个值的函数,包含四个参数:
      `accumulator`
      累加器累加回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(如下所示)。

      `currentValue`
      数组中正在处理的元素。
      `currentIndex`
      数组中正在处理的当前元素的索引。 如果提供了initialValue,则索引号为0,否则为索引为1。
      `array`
      调用reduce的数组
      `initialValue`
      可选,用作第一个调用callback的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用 reduce 将报错。
      返回值: 函数累计处理的结果

      var total = [0, 1, 2, 3].reduce(function(sum, value) {
      return sum + value;
      }, 0);
      // total is 6

      var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
      return a.concat(b);
      }, []);
      // flattened is [0, 1, 2, 3, 4, 5]

      // 使用 reduce 表示 map
      var a = [1,2,3]
      a.map(function(value){
      return value * 2
      })

      a.reduce(function(arr, n){
      arr.push(n*2)
      return arr
      }, [])

      // 使用 reduce 表示 filter
      var b = [1,2,3,4,5,6,7,8,9,10]
      b.filter(function(value){
      return value % 2 === 0
      })

      b.reduce(function(arr, n){
      if(n % 2 === 0){

      arr.push(n)
      

      }
      reruen arr
      },[])

      1
      2
      3


      - ## `Function` -[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function)

    // 常用简写方式
    var f = function(a,b){
    return a+b
    }

    var f = new Function(‘a’, ‘b’, ‘return a+b’) //使用 new 进行构造,
    var f = new Function([‘a’,’b’],’return a+b’)

    1
    2
    3
    4
    5
    6
    7
    8
    语法:`new Function ([arg1[, arg2[, ...argN]],] functionBody)`
    参数:
    `arg1, arg2, ... argN`
    被函数使用的参数的名称必须是合法命名的。参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表;例如“×”,“theValue”,或“A,B”。
    `functionBody`
    一个含有包括函数定义的JavaScript语句的字符串

    函数总是会返回一个值

    function fn(){
    console.log(‘函数’)
    return undefined // 如果你不写就会自动添加这样的一句
    }
    ```

123

zerolhao

28 posts
2 categories
43 tags
© 2018 zerolhao
Powered by Hexo
|
Theme — NexT.Muse v5.1.4