myBlog


  • Home

  • Archives

AJAX

Posted on 2019-03-15

AJAX

异步的 JavaScript 和 XML

  1. 使用 XMLHttpRequest 发请求
  2. 服务器返回 XML 格式的字符串
  3. JS 解析 XML,并更新局部页面

原生 JS 实现AJAX 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
var request = new XMLHttpRequest()
request.open('GET', '/XXXX')
request.readystatechange = function() {
if(request.readyState === 4) {
console.log('请求完成')
if(request.response.status >= 200 && request.response.status < 300) {
console.log('请求成功')
} else if(request.status >= 400) {
console.log('请求失败')
}
}
}
request.send()

AJAX 的所有功能

  • 客户端的JS发起请求(浏览器上的)
  • 服务端的JS发送响应(Node.js上的)
  1. JS 可以设置任意请求 header
  • 第一部分 request.open(‘get’, ‘/xxx’)
  • 第二部分 request.setRequestHeader(‘content-type’,’x-www-form-urlencoded’)
  • 第四部分 request.send(‘a=1&b=2’)
  1. JS 可以获取任意响应 header
  • 第一部分 request.status / request.statusText
  • 第二部分 request.getResponseHeader() / request.getAllResponseHeaders()
  • 第四部分 request.responseText

模仿 jQuery 封装一个 AJAX

封装代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
window.$ = window.jQuery

window.jQuery.ajax = function({url, method, body, successFn, failFn, headers}){
let request = new XMLHttpRequest()
request.open('method', 'url)
for(let key in headers) {
let value = headers[key]
request.setRequestHeader(value, key)
}
request.readystatechange === function() {
if(request.readyState === 4) {
if(request.response.status >= 200 &&
request.response.status < 300){
successFn.call(undefined, request.responseText)
} else if(request.response.status >= 400) {
failFn.call(undefined, request)
}
}
}
request.send(body)
}

使用封装好的 jQuery.ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function f1(responseText){}
function f2(responseText){}

myButton.addEventListener('click', (e)=>{
$.ajax({
url: '/frank',
method: 'get',
headers: {
'content-type':'application/x-www-form-urlencoded',
'frank': '18'
},
successFn: (x)=>{
f1.call(undefined,x)
f2.call(undefined,x)
},
failFn: (x)=>{
console.log(x)
console.log(x.status)
console.log(x.responseText)
}
})
})

升级jQuery.ajax 满足 Promise 规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
window.$ = window.jQuery

window.jQuery.ajax = function({url, method, body, successFn, failFn, headers}){
return new Promise(function(resolve, reject){
let request = new XMLHttpRequest()
request.open('method', 'url)
for(let key in headers) {
let value = headers[key]
request.setRequestHeader(value, key)
}
request.readystatechange === function() {
if(request.readyState === 4) {
if(request.response.status >= 200 &&
request.response.status < 300){
successFn.call(undefined, request.responseText)
} else if(request.response.status >= 400) {
failFn.call(undefined, request)
}
}
}
request.send(body)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
myButton.addEventListener('click', (e)=>{
let promise = window.jQuery.ajax({
url: '/xxx',
method: 'get',
headers: {
'content-type':'application/x-www-form-urlencoded',
'frank': '18'
}
})

promise.then(
(text)=>{console.log(text)},
(request)=>{console.log(request)}
)

})

JSONP

Posted on 2019-03-14

1. 什么是 JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个\<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
具体用法:
请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)

  1. 请求方创建 script,src 指向响应方,同时传一个查询参数 ?callbackName=yyy
  2. 响应方根据查询参数callbackName,构造形如
    i. yyy.call(undefined, ‘你要的数据’)
    ii. yyy(‘你要的数据’)
    这样的响应
  3. 浏览器接收到响应,就会执行 yyy.call(undefined, ‘你要的数据’)
  4. 那么请求方就知道了他要的数据
    这就是 JSONP
    约定:
  5. callbackName -> callback
  6. yyy -> 随机数 frank12312312312321325()
    jQuery 用法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $.ajax({
    url: "http://jack.com:8002/pay",
    dataType: "jsonp",
    success: function( response ) {
    if(response === 'success'){
    amount.innerText = amount.innerText - 1
    }
    }
    })

2. JSONP 为什么不支持 POST

1.JSONP 是通过动态创建 script 实现的。
2 动态创建 script 的时候只能用 GET ,不能用 POST。

JSONP-demo

常见 HTTP 状态码

Posted on 2019-03-11

1XX 代表提示信息

  • 100 Continue
    客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。
  • 101 Switching Protocols
    服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。
  • 102 Processing
    WebDAV请求可能包含许多涉及文件操作的子请求,需要很长时间才能完成请求。该代码表示​​服务器已经收到并正在处理请求,但无响应可用。

2XX 代表成功信息

  • 200 OK
    请求已成功,请求所希望的响应头或数据体将随此响应返回。
  • 201 Created
    请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回。假如需要的资源无法及时建立的话,应当返回 ‘202 Accepted’。
  • 202 Accepted
    服务器已接受请求,但尚未处理。最终该请求可能会也可能不会被执行,并且可能在处理发生时被禁止。
  • 203 Non-Authoritative Information
    服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。
  • 204 No Content
    服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。
  • 205 Reset Content
    服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。
  • 206 Partial Content
    服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。

3XX代表重定向信息

  • 300 Multiple Choices
    被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。
  • 301 Moved Permanently
    被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。
  • 302 Move temporarily
    请求的资源临时从不同的 URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。
  • 303 See Other
    对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。
  • 304 Not Modified
    如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。

    4XX代表客户端错误信息

  • 400 Bad Request
    • 语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
    • 请求参数有误。
  • 401 Unauthorized
    当前请求需要用户验证。
  • 403 Forbidden
    服务器已经理解请求,但是拒绝执行它。
  • 404 Not Found
    请求失败,请求所希望得到的资源未被在服务器上发现。
  • 405 Method Not Allowed
    请求行中指定的请求方法不能被用于请求相应的资源。
  • 408 Request Timeout
    请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。

5XX代表服务器错误信息

  • 500 Internal Server Error
    服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。
  • 501 Not Implemented
    服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
  • 502 Bad Gateway
    作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
  • 505 HTTP Version Not Supported
    服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。
  • 508 Loop Detected
    服务器在处理请求时陷入死循环。

call,apply,bind 的区别

Posted on 2019-03-09

apply call bind 是用来给函数指定 this 。

  1. call
    第一个参数是函数里面的 this,第 2,3,4,5… 个参数依次作为参数传给函数。

    1
    2
    3
    4
    function sum(a, b){
    return a + b
    }
    sum.call(sum, 1, 2)//3
  2. apply
    apply 和 call 类似,第一个参数是函数里面的 this,第二个参数是要传给函数的参数列表,类型是一个伪数组,apply 会把这个伪数组拆成一个个的参数传给函数。

    1
    2
    3
    4
    function sum(a, b){
    return a + b
    }
    sum.apply(sum, [1, 2])//3
  3. bind
    bind接受的参数和 call 一致,但是函数不直接调用,而是返回一个函数,在需要的时候调用。

    1
    2
    3
    4
    5
    function sum(a, b){
    return a + b
    }
    var newSum = sum.bind(sum, 1, 2)
    newSum() //3

关于闭包的部分理解

Posted on 2019-03-09

什么是闭包

如果一个函数,用了它范围外的变量,那么(这个函数 + 这个变量)就叫做闭包,闭包有权访问其包含函数内部的所有变量,闭包的作用域链包含自己的作用域、包含函数的作用域和全局作用域。当函数返回一个闭包时,这个函数的作用域将会一直存在于内存中,直到闭包不存在。

闭包的作用

  1. 模仿块级作用域(匿名自执行函数)

    1
    2
    3
    4
    5
    6
    function countNumber(n) {
    for(var i = 0; i < n; i++) {
    console.log('i1',i)
    }
    console.log('i2',i)
    } // i2 5
    1
    2
    3
    4
    5
    6
    7
    8
    function countNumber(n) {
    (function(){
    for(var i = 0; i < n; i++) {
    console.log('i1',i)
    }
    })()
    console.log('i2',i)
    } // i2 i is not defined

    在第二个函数中,在 for 循环外部插入了一个块级作用域。原因是在匿名函数中定义的任何变量,都会在执行结束时被销毁,因此变量 i 只能在循环中使用,使用后即被销毁。这个块级作用域能访问变量 n,是因为这个匿名函数是一个闭包,它能够访问包含作用域中(也就是 countNumber 函数)中的所有变量。这个方法可以尽量避免在全局环境中添加过多变量和函数。也可以减少闭包占用内存的问题,因为没有指向匿名函数的引用。

  2. 访问私有变量
    任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

    1
    2
    3
    4
    function add(n1, n2){
    var sum = n1 + n2
    return sum
    }

    在这个函数内部,有3个私有变量:n1、n2 和 sum。函数内部可以访问这几个变量,但是函数外部则不能访问它们。如果在这个函数内部创建一个闭包,则可以通过闭包访问这些变量。

    • 从外部读取函数内部的变量

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function fn1() {
      var n = 1
      function fn2() {
      return n
      }
      return f2
      }
      var result = f1()
      result() //1
    • 对象的继承

      • 构造函数定义公有方法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        function MyObject() {
        //私有变量和私有函数
        var privateVariable = 10

        function privateFunction() {
        return false
        }
        //公有方法
        this.variable = function() {
        return privateVariable++
        }
        this.function = function() {
        return privateFunction()
        }


        }

        var obj = new MyObject()
        obj.variable()// 10
        obj.variable()// 11
        obj.function()// false

        利用这个方法,可以隐藏不想被直接修改的数据,比如:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        function Person(name) {
        this.getName = function() {
        return name
        }
        this.setName = function(value) {
        name = value
        }
        }

        var person = new Person('yang')
        person.getName() //yang
        person.setName('zhang')
        person.getName() //zhang

        在上述代码中,定义了两个公有方法:getName() 和 setName()。这两个函数都可以在函数外部使用,而且它们都是在构造函数内部定义的,作为闭包都可以访问私有变量 name。在构造函数外部,没有任何办法访问到name。需要注意的是这个方法的私有变量 name 在每个 Person 实例中都会不同,因为每次调用构造函数都会重新创建这两个方法。

        • 原型模式(静态私有变量)
  3. 结果缓存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function count(n) {
    return function () {
    return n++;
    };
    }
    var c1 = count(1);
    c1() // 1
    c1() // 2
    c1() // 3

    闭包的一个特点就是只要存在引用函数内部变量的可能,JavaScript就需要在内存中保留这些变量。而且JavaScript运行时需要跟踪这个内部变量的所有外部引用,直到最后一个引用被解除(主动把外部引用赋为 null 或者页面关闭),JavaScript的垃圾收集器才能释放相应的内存空间,所以闭包不会释放外部的引用,从而函数内部的值可以得以保留。

DOM事件模型相关笔记

Posted on 2019-03-07

DOM 事件

目前DOM 2 是目前使用最广泛的标准。

  1. DOM 1 方法:
  • 在 HTML 中:

    1
    2
    3
    4
    5
    var print = function() {
    cosole.log('hi')
    }
    <button id=Y onclick="print()">A</button>
    <button id=Z onclick="print.call()">A</button>

    onclick=”要执行的代码”,一旦用户点击,浏览器就 eval(“要执行的代码”)。

  • 在 JS 中:

    1
    2
    3
    4
    5
    6
    var print = function() {
    cosole.log('hi')
    }
    X.onclick = print //类型为函数对象,应该用这种写法。
    X.onclick = print() //类型为 undefined
    X.onclick = print.call() //类型为 undefined

    一旦用户点击,就执行X.onclick.call(this,arguments)

  1. DOM 2 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>event</title>
    </head>
    <body>
    <button id='xxx'>xxx</button>
    </body>
    </html>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    xxx.addEventListener('click', function(){
    console.log(1)
    })
    xxx.addEventListener('click', function(){
    console.log(2)
    })
    //xxx拥有一个队列 addeventlisteners,先进去的函数先触发,不会覆盖别的事件。
    function f1() {
    console.log(1)
    }
    function f2() {
    console.log(2)
    }
    xxx.addEventListener('click', f1)
    xxx.addEventListener('click', f2)

    jQuery中.one的实现原理

    1
    2
    3
    4
    5
    6
    7
    function f1() {
    console.log(1)
    xxx.removeEventListener('click', f1)

    }
    xxx.addEventListener('click', f1)
    //执行一次后,栈内的函数被 remove,该事件只会被触发一次。
  2. DOM 0 :标准出现之前的事实规范

  3. 事件冒泡和事件捕获
    当点击儿子的时候,父亲和爷爷同时被点击

    fn1 fn2 fn3 的执行顺序由事件的第三个参数决定。默认情况下,或者第三个参数为 falsy 值,是由下往上执行(事件冒泡)。


    当第三个参数为true ,从上往下执行(事件捕获)。


    不同事件第三个参数的值不同时,会先进行事件捕获(true),然后再事件冒泡(flase)。


    如果同一个事件上绑定了两个不同的值时,执行顺序由代码的先后顺序执行。

用原生 JS 实现 jQuery

Posted on 2019-03-05

实现getSiblings和addClass的功能

网页HTML部分:

1
2
3
4
5
6
7
8
9
10
<body>
<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>
<li id="item6">选项6</li>
</ul>
</body>

  • getSiblings

    1
    2
    3
    4
    5
    6
    7
    8
    let allChildren = item3.parentNode.children 
    let array = {length: 0}
    for(let i = 0; i < allChildren.length; i++){
    if(allChildren[i] !== item3){
    array[array.length] = allChildren[i]
    array.length += 1
    }
    }
  • addClass

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let classes = {'a': true, 'b': false, 'c': true}
    for(let key in classes){
    let value = classes[key]
    if(value){
    item3.classList.add(key)
    }else{
    item3.classList.remove(key)
    }
    }

封装原生函数

1
2
3
4
5
6
7
8
9
10
11
12
function getSiblings(node){
let allChildren = node.parentNode.children
let 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
}
getSiblings(item3)
1
2
3
4
5
6
7
8
function addClass(node, classes){
for(let key in classes){
let value = classes[key]
let methodName = value ? 'add' : 'remove'
node.classList[methodName](key)
}
}
addClass(item3, {'a': true, 'b': false, 'c': true})

命名空间

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 getSiblings(node){
let allChildren = node.parentNode.children
let 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
}

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

window.ffDOM = {}
ffDOM.getSiblings = getSiblings
ffDOM.addClass = addClass

ffDOM.getSiblings(item3)
ffDOM.addClass(item3, {'a': true, 'b': false, 'c': true})

扩展 Node 接口

直接在 Node.prototype 上加函数

1
2
3
4
5
6
7
8
9
10
11
12
13
Node.prototype.getSiblings = function(){
let allChildren = this.parentNode.children
let 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
}

item3.getSiblings()

1
2
3
4
5
6
7
8
Node.prototype.addClass = function(classes){
for(let key in classes){
let value = classes[key]
let methodName = value ? 'add' : 'remove'
this.classList[methodName](key)
}
}
item3.addClass({'a': true, 'b': false})

无侵入添加 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
26
window.Node2 = function(node){
return {
getSiblings: function(){
let allChildren = node.parentNode.children
let 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 value = classes[key]
let methodName = value ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}

var node2 = Node2(item3)
node2.getSiblings()
node2.addClass({'a': true, 'b': false, 'c': true})

添加接受选择器功能

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
window.jQuery = function(nodeOrSelector){
let node
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
}else{
node = nodeOrSelector
}
return {
getSiblings: function(){
let allChildren = node.parentNode.children
let 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 value = classes[key]
let methodName = value ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}

var node2 = jQuery('#item3')
node2.getSiblings()
node2.addClass({'a': true, 'b': false, 'c': true})

添加接受多个选择器功能

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
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]
}
nodes.length = temp.length
}else if(nodeOrSelector instanceof Node){
nodes = {0: nodeOrSelector, length: 1}
}
nodes.addClass = function(classes){
for(let key in classes){
let value = classes[key]
let methodName = value ? 'add' : 'remove'
for(let i = 0; i < nodes.length; i++){
nodes[i].classList[methodName](key)
}
}
}
nodes.text = function(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({'a': true, 'b': false, 'c': true})
node2.text('hi')

div 和 $div 的联系和区别

1.区别:
$div 是一个 jQuery 对象,可以使用 jQuery 的的属性方法,比如addClass removeClass toggleClass 等
(一般情况下使用 jQuery 获取到的元素应该在变量命名时在前边加上 $ 符号以便区分。)
div 是一个 DOM 对象,可以使用 DOM API 的属性和方法,比如firstChid、parentNode、appendChild等。
2.联系:
使用$(div) 可以将 div 封装成一个 jQuery 对象。
使用$(div)[0]获取到的第一项就是 div,所以可以使用这种方法将 $div 变成 div。

函数相关

Posted on 2019-03-02

1. 函数的五种声明方式

  1. 具名函数

    1
    2
    3
    4
    function f(x,y){
    return x+y
    }
    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
    var f
    f = function f2(x,y){ return x+y }
    f.name // 'f2'
    console.log(f2) // undefined
  4. window.Function

    1
    2
    var f = new Function('x','y','return x+y')
    f.name // "anonymous" //匿名
  5. 箭头函数

    1
    2
    3
    4
    5
    var f = (x,y) => {
    return x+y
    }
    var sum = (x,y) => x+y
    var n2 = n => n*n

2. 调用函数

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

3. this 和 arguments

1
2
3
4
5
6
7
function f(){
'use strict'
console.log(this)
console.log(arguments)
return undefined
}
f.call(1,2,3) // this 为 1,arguments 为 [2,3]

方应杭老师关于 this 的解释

3. 作用域

  • 按照语法树,就近原则。
  • 我们只能确定变量是哪个变量,但是不能确定变量的值。
  • 3个关于作用域的面试题:拿到代码请先提升声明。
    面试题1

    1
    2
    3
    4
    5
    6
    var a = 1
    function f1(){
    alert(a) // 是多少
    var a = 2
    }
    f1.call()

    面试题2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var a = 1
    function f1(){
    var a = 2
    f2.call()
    }
    function f2(){
    console.log(a) // 是多少
    }
    f1.call()

    面试题3

    1
    2
    3
    4
    5
    6
    var liTags = document.querySelectorAll('li')
    for(var i = 0; i<liTags.length; i++){
    liTags[i].onclick = function(){
    console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
    }
    }

3.闭包

如果一个函数,用了它范围外的变量,那么(这个函数 + 这个变量)就叫做闭包。
方应杭老师关于闭包的解释

Array and Function

Posted on 2019-02-28

构造函数相关

基本类型的数据(number string boolean)的构造函数:

  • Number(a) String(a) Boolean(a) 返回一个转换后的基本类型的数据。
  • new Number(a) new nString(a) new Boolean(a) 返回一个对象。
    复杂类型的构造函数:
  • let a = Array(1) === let a = new Array(1)
  • let o = Object(1) === let o = new Object(1)
    函数的构造函数:
  • var f = new Function('a', 'b', 'return a + b') === var f = Function('a', 'b', 'return a + b')最后一个参数为函数体。
    1
    2
    3
    var f = function(a,b) {
    return a + b
    }
1
var f = new Function('a', 'b', 'return a + b')
上边两种创建函数的方法结果相等。

Array

  1. 不一致性,当 Array 只有一个参数时,表示的是创建的数组的长度。

    1
    2
    3
    let a = Array(3)
    a.length //3
    a //[empty * 3] length: 3
    1
    2
    3
    let a = Array(3,3)
    a.length //2
    a //[3,3] length: 2
  2. 伪数组:原型链中没有Array.prototype 属性。

  3. a.forEach( function(x,y){
    } ):参数接受一个函数,函数接受两个(三个)参数。

原型与原型链

Posted on 2019-02-24

原型(公用属性)

在 javascript 中,函数都是对象,可以有属性。 每个函数都有一个特殊的属性叫作原型(prototype),这个属性会指向函数的原型对象。JavaScript 的原型对象中,还包含了一个 constructor 属性,这个属性对应创建所有指向改原型的实例的构造函数。

每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性。

在使用构造函数生成实例的时候,prototype 属性会成为该对象实例的原型。constructor 指向该实例的构造函数(prototype 属性所在的函数)。

上述代码中,Foo 为构造函数,p1 与 p2 是该构造函数的实例对象,Foo 的 prototype 属性就是这两个实例的原型对象,所以它们都可以使用.age 方法,这个方法在两个实例的__proto__属性中。constructor 属性指向含有该原型的构造函数 Foo。

实例对象.__proto__ === 函数.prototype


原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。这是因为实例对象其实没有age属性,都是读取原型对象的age属性。当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。

如果某个实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法,所以改变原型对象上的值也不会影响该实例。
构造函数生成的实例只有__proto__属性,没有prototype 属性。

原型链

  • 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object. prototype中依然没有找到,则返回undefined。
    Object是JS中所有对象数据类型的根基,在Object.prototype上的__proto__属性指向null。

  • 重要公式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var 对象 = new 函数()
    对象.__proto__ === 对象的构造函数.prototype

    // 推论
    var number = new Number()
    number.__proto__ = Number.prototype
    Number.__proto__ = Function.prototype // 因为 Number 是 Function 的实例

    var object = new Object()
    object.__proto__ = Object.prototype
    Object.__proto__ = Function.prototype // 因为 Object 是 Function 的实例

    var function = new Function()
    function.__proto__ = Function.prototype
    Function.__proto__ == Function.prototye // 因为 Function 是 Function 的实例!
  • __proto__ 和 prototype 的区别是什么?

    1. 不能断章取义,__proto__和 prototype 只是两个 key 而已。
    2. 我们一般研究对象的__proto__和函数的prototype。
    3. 对象.__proto__ === 某函数.prototype。
    4. 如果把函数看成对象,那么函数.__proto__ === Function.prototype。
    5. 如果把 Function 看成对象,那么 Function.__proto__ === Function.prototype。
123

yang

25 posts
1 tags
© 2019 yang
Powered by Hexo
|
Theme — NexT.Muse v5.1.4