梁光静


  • Home

  • Archives

cookie、 session、localStorage入门

Posted on 2018-08-31

1. cookie和session的区别

  • 一般来说,Session 基于 Cookie 来实现。

    1. cookie

  1. 服务器通过 Set-Cookie 头给客户端一串字符串
  2. 客户端每次访问相同域名的网页时,必须带上这段字符串
  3. 客户端要在一段时间内保存这个Cookie
  4. Cookie 默认在用户关闭页面后就失效,后台代码可以任意设置 Cookie 的过期时间
  5. 大小大概在 4kb 以内

    2. session

  6. 将 SessionID(随机数)通过 Cookie 发给客户端
  7. 客户端访问服务器时,服务器读取 SessionID
  8. 服务器有一块内存(哈希表)保存了所有 session
  9. 通过 SessionID 我们可以得到对应用户的隐私信息,如 id、email
  10. 这块内存(哈希表)就是服务器上的所有 session

    2. localStorage

  11. LocalStorage 跟 HTTP 无关
  12. HTTP 不会带上 LocalStorage 的值
  13. 只有相同域名的页面才能互相读取 LocalStorage(没有同源那么严格)
  14. 每个域名 localStorage 最大存储量为 5Mb 左右(每个浏览器不一样)
  15. 常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)
  16. LocalStorage 永久有效,除非用户清理缓存

    3. SessionStorage(会话存储)

  • 1、2、3、4 同上
  1. SessionStorage 在用户关闭页面(会话结束)后就失效。

自己封装AJAX

Posted on 2018-08-28
  1. 用原生 JS 写出一个 AJAX 请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let request = new XMLHttpRequest()
    request.open('get', '/xxx') // 配置request
    request.send()
    request.onreadystatechange = ()=>{
    if(request.readyState === 4){
    if(request.status >= 200 && request.status < 300){
    console.log('说明请求成功')
    }else if(request.status >= 400){
    console.log('说明请求失败')
    }
    }
    }
  2. 自己封装一个封装一个 jQuery.ajax

jQuery.ajax(url,method,body,success, fail)

满足这种 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
39
40
41
42
43
44
45
46
47
48
49
window.jQuery = function(nodeOrSelector){
let nodes = {}
nodes.addClass = function(){}
nodes.html = function(){}
return nodes
}
window.$ = window.jQuery

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

function f1(responseText){}
function f2(responseText){}

myButton.addEventListener('click', (e)=>{
window.jQuery.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)
}
})
})

  1. 升级刚刚的 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
    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
    window.jQuery = function(nodeOrSelector){
    let nodes = {}
    nodes.addClass = function(){}
    nodes.html = function(){}
    return nodes
    }
    window.$ = window.jQuery

    window.Promise = function(fn){
    // ...
    return {
    then: function(){}
    }
    }

    window.jQuery.ajax = function({url, method, body, headers}){
    return new Promise(function(resolve, reject){
    let request = new XMLHttpRequest()
    request.open(method, url) // 配置request
    for(let key in headers) {
    let value = headers[key]
    request.setRequestHeader(key, value)
    }
    request.onreadystatechange = ()=>{
    if(request.readyState === 4){
    if(request.status >= 200 && request.status < 300){
    resolve.call(undefined, request.responseText)
    }else if(request.status >= 400){
    reject.call(undefined, request)
    }
    }
    }
    request.send(body)
    })
    }

    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)}
    )

    })

DOM面试

Posted on 2018-08-25

1. DOM事件模型

  1. 冒泡
  2. 捕获
  3. 如果这个元素是被点击元素,那么执行顺序由监听顺序决定

2. 移动端的触摸事件

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    //说明触摸开始
    document.body.ontouchstart !== undefined
    //触摸开始移动
    canvas.ontouchmove = function (ev) {
    var x = ev.touches[0].clientX; //触摸的x坐标
    var y = ev.touches[0].clientY;
    }
    ontouchend //触摸结束
    ontouchcancel //触摸取消,因为有的浏览器不会触发touchend事件
  2. 模拟swipe事件(滑动)
    记录两次touchmove的位置差,若后一次在前一次的右边,说明向右滑了

3. 事件委托

  1. 监听父元素,看被触发的是哪个儿子,就是事件委托,委托父亲监听儿子
  2. 优点是可以监听动态生成的儿子,减少了事件处理程序的数量,整个页面占用的内存空间会更少。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function listen(element,eventType,selector,fn){
    element.addEventListener(eventType,(e)=>{
    let el = e.target
    while(!el.matches(selector)){ //可能点击的是li里的span
    if(element === el){ //如果点击的是父元素,则跳出监听
    el =null
    break
    }
    el = el.parentNode
    }
    el&&fn.call(el,e,el)
    })
    return element
    }

    //<ul id="list">
    // <li></li>
    // ...
    //</ul>
    //let list=document.querySelector('#list')
    //listen(list,'click','li',()=>{})

DOM事件

Posted on 2018-08-25

1. DOM0时代html和js的那些事儿

<html>

1
2
3
4
5
6
7
8
9
10
11
12
<head>
<script>
function print() {
console.log('hi');
}
</script>
</head>
<body>
<button id="x" onclick="print">a</button> //html里的onclick相当于eval('print') ,这样只会打印出print这个函数
<button id="y" onclick="print()">b</button> //'hi'
<button id="z" onclick="print.call()">c</button> //'hi'
</body>

<js>

1
2
3
x.onclick = print;  //js里这样是执行函数,输出'hi'
x.onclick = print(); //这样写是得到函数的返回值,undefined
x.onclick = print.call(); //同上,undefined

2. DOM2

1. 监听事件

1
2
3
4
5
6
7
xxx.onclick = function(){};  //不要用这个,因为可能会覆盖掉别人的事件
//用下面这个

function f1(){}

xxx.addEventListener('click',f1) //EventListener是队列,先绑定的就先触发
xxx.removeEventListener('click',f1);

若想让事件被点一次后就取消监听,可以将f1写成下面这样

1
2
3
4
5
6
function f1(){
console.log(1);
xxx.removeEventListener('click',f1); //这样f1执行一次后就自动取消xxx的监听,
}

xxx.addEventListener('click',f1)

这就是$btn.one(‘click’,fn);

2. 嵌套的div被点击的顺序

HTMLJavaScript

  • 面试题1:请问点击儿子的div,这三个函数的执行顺序是什么?
    内部执行顺序
    默认的顺序是像右边的箭头序列,先最里面的儿子再爸爸再爷爷,但如果给addEventListener加个true的话,事件就会到左边来,如下
    1
    2
    3
    grand1.addEventListener('click',function fn1(){
    console.log('爷爷')
    },true)

addEventListener(true)
这样执行顺序就是爷爷 -> 儿子 -> 爸爸。
左边的(加true的顺序),叫做捕获阶段,右边叫冒泡阶段。

  • 面试题2:若给同一个div加上true和false呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    child1.addEventListener('click',function fn1(){
    console.log('儿子冒泡')
    },false)
    child1.addEventListener('click',function fn1(){
    console.log('儿子捕获')
    },true)
    //点击child出现'儿子捕获','儿子冒泡'
    //若是给被点击的本身加上true和false,则按事件添加的顺序执行
    //若给爸爸或是爷爷添加true和false,则是按上图箭头顺序执行
  • stopPropagation()
    这个方法可以让它那一层停止往上传播

    1
    2
    3
    child1.addEventListener('click',function fn1(e){
    e.stopPropagation();
    }) //这样点击儿子,就只会执行儿子,不会执行爸爸和爷爷

3. 点击别处关闭浮层

html

1
2
3
4
5
6
<div id="wraper" class="wraper">
<button id="clickMe">点我</button>
<div id="popover" class="popover">
<input type="checkbox">浮层
</div>
</div>

错误示范1,js

1
2
3
4
5
6
7
8
$(clickMe).on('click',function () {
$(popover).show();
});
$(wrapper).on('click',false);
//这里有错误,浮层出来后无法点击checkbox,因为这里的false,阻止了所有默认事件,应该改成下面那样
$(document).on('click',function () {
$(popover).hide();
});

更新版1,还是不完美

1
2
3
4
5
6
7
8
9
$(clickMe).on('click',function () {
$(popover).show();
});
$(wrapper).on('click',function(e){
e.stopPropagation(); //阻止冒泡
});
$(document).on('click',function () {
$(popover).hide();
}); //这样若有多个浮层,则会监听多次document,浪费资源,可以改成下面的

错误示范2,js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(clickMe).on('click',function () {
$(popover).show();
$(document).one('click',function () { //改为one
$(popover).hide();
});
}); //若没有阻止冒泡,浮层不会显示出来,因为hide也会执行

//可以添加一个setTimeout使它不会马上执行,如下
$(clickMe).on('click',function () {
$(popover).show();
setTimeout(function(){
$(document).one('click',function () {
$(popover).hide();
});
},0) //添加setTimeout后,document.onclick不会马上执行,当下一次点击屏幕的时候才会执行
//这样还是有bug,点击popover内部也会执行hide()
});

完美版本1

1
2
3
4
5
6
7
8
9
$(clickMe).on('click',function () {
$(popover).show();
$(document).one('click',function () {
$(popover).hide();
}); //只在浮层show的时候监听document一次
});
$(wrapper).on('click',function(e){
e.stopPropagation(); //阻止冒泡,这样点击popover内部就不会执行document.onclick,但这个事件还是会存放在那里
});

完美版本2
更新上个版本点按钮不会关闭浮层的bug

1
2
3
4
5
6
7
8
9
10
11
12
13
$(clickMe).on('click',function () {
if($(popover).is(':hidden')){ //若浮层是关闭的,点击按钮则打开
$(popover).show();
}else {
$(popover).hide(); //和上面相反
}
$(document).one('click',function () {
$(popover).hide();
});
});
$(wrapper).on('click',function(e){
e.stopPropagation();
});

理解jQuery的实现过程

Posted on 2018-08-22

自己封装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
32
33
34
35
36
window.jQuery = ???
window.$ = jQuery

var $div = $('div')
$div.addClass('red') // 可将所有 div 的 class 添加一个 red
$div.setText('hi') // 可将所有 div 的 textContent 变为 hi

<-*************->


window.jQuery = function (x) {
let nodes = {} //选出div并转为伪数组
let temp = document.querySelectorAll(x)
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
nodes.length = temp.length
}

nodes.addClass = function (y) { //函数实现遍历div加上class的功能
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.add(y)
}
}

nodes.setText = function (text) { //函数实现遍历div加上innerText的功能
for (let i = 0; i < nodes.length; i++) {
nodes[i].innerText = text
}
}

return nodes
}
window.$ = jQuery
var $div = $('div')
$div.addClass('red')
$div.setText('hi')

代码实现过程

1
window.jQuery = function(nodeOrSelector)
  • 一般我们的得到的元素都是一个dom对象,而这个对象最终继承的就是Node接口
  • 所以可以在 Node.prototype 上添加以上 addClass 及setText 方法
  • 但是Node.prototype 是一个共有对象,每个人写的程序都不一样, 如果大家都在这里面操作添加删除API , 就会出现各种问题
  • 所以我们可以取一个我们自己喜欢的名字来当作我们简便操作dom树,这个构造函数就不会被别人乱修改啦,而jQuery的作者就取名叫jQuery的
  • 为了更懒更简便的操作,所以让$ = jQuery

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let nodes = {}
    if (typeof nodeOrSelector === 'string') {
    let tmp = document.querySelectorAll(nodeOrSelector);
    for(var i =0; i < tmp.length; i++){
    nodes[i] = tmp[i];
    }
    nodes.length = i;
    } else if (nodeOrSelector instanceof Node) {
    nodes = {
    0: nodeOrSelector,
    length: 1
    }
    }
  • 因为jQuery 传入的有可能是字符串形式的选择器 或者 节点元素,所以 这段代码实现的功能是

  • 先创建一个对象
  • 判断传入的参数是字符串还是dom对象
  • 如果是字符串就document.querySelectorAll匹配所有符合的选择器的元素,然后改成jQuery形式的对象,也就是我们开始创建的对象,不能直接复制给这个对象,因为匹配得到的对象是NodeList对象,是dom类型对象
  • 如果是节点元素就直接添加进第一项就好了
  • 因为jQuery ,$div[0] === div

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    nodes.addClass = function(classs) {
    for (let i = 0; i < nodes.length; i++) {
    nodes[i].classList.add(classs);
    }
    }
    nodes.setText = function(text){
    for (let i = 0; i < nodes.length; i++) {
    nodes[i].textContent = text;
    }
    }
    return nodes;
  • 最后就是给这个要返回的对象添加API啦

  • 真正的jQuery的API都是封装在jQuery.prototype中,所有的jQuery对象都共用这些API
  • 这里的nodes就与两个方法之间产生了闭包, 保护了nodes的私有化

    1
    2
    3
    4
    5
    window.$ = jQuery

    var $div = $('div')
    $div.addClass('red') // 可将所有 div 的 class 添加一个 red
    $div.setText('222') // 可将所有 div 的 textContent 变为 hi
  • 为了更方便的操作 用$代替jQuery ,var $div = $('div') 可以 var $div = jQuery('div') 也是一样的

  • 构造函数返回的对象就包含了两个方法, 我们就可以直接调用啦。

页面从输入 URL 到页面加载显示完成的过程

Posted on 2018-08-21

1. 浏览器查找域名对应的 IP 地址:

通过DNS查询到相应的IP地址,相当于你的路由问电信公司,于是电信公司返回对应的IP地址。
补充说明:

  • 你若修改了hosts文件,可自己指定域名的IP,绕过DNS。
  • URL和域名是两个概念:例如,xiedaimala.com是这个网站的域名,根据这个域名找到写代码啦的服务器,xiedaimala.com/courses 是URL,根据这个URL定位到写代码啦的课程表。
  • IP 地址与域名不是一一对应的关系:可以把多个提供相同服务的服务器 IP 设置为同一个域名,但在同一时刻一个域名只能解析出一个 IP地址;同时,一个 IP 地址可以绑定多个域名,数量不限;

2. 浏览器和服务器建立连接——三次握手(采用TCP协议)

  • 浏览器说:服务器我要连接你了,可以吗
  • 服务器说:好的,连接吧
  • 浏览器说:好的,那我连接了
    连接成功!

3. 网页的请求与显示

  1. 浏览器向服务器发送请求的第一部分
  2. 浏览器向服务器发送请求头(第二部分),若是post请求还会发送请求体(第四部分)
  3. 服务器应答,发送响应的第一部分
  4. 服务器发送响应的响应头信息
  5. 服务器发送以Content-type描述的格式的数据
  6. 在浏览器还没有完全接收 HTML 文件时便开始渲染、显示网页
  7. 在执行 HTML 中代码时,根据需要,浏览器会继续请求图片、CSS、JavsScript等文件,过程同请求 HTML

4. 断开连接——四次握手

  • 主机向服务器发送一个断开连接的请求(不早了,我该走了);

  • 服务器接到请求后发送确认收到请求的信号(知道了);

  • 服务器向主机发送断开通知(我也该走了);

  • 主机接到断开通知后断开连接并反馈一个确认信号(嗯,好的),服务器收到确认信号后断开连接;

补充说明:

  • 为什么服务器在接到断开请求时不立即同意断开:当服务器收到断开连接的请求时,可能仍然有数据未发送完毕,所以服务器先发送确认信号,等所有数据发送完毕后再同意断开。
  • 第四次握手后,主机发送确认信号后并没有立即断开连接,而是等待了 2 个报文传送周期,原因是:如果第四次握手的确认信息丢失,服务器将会重新发送第三次握手的断开连接的信号,而服务器发觉丢包与重新发送的断开连接到达主机的时间正好为 2 个报文传输周期。

未完待续…

JS函数

Posted on 2018-08-20

1. 函数的声明方式

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
1.function a(a,b){
return a+b;
}

2. var a = function(a,b){ //匿名函数,函数表达式
return a+b;
}
a(1,2) //3

3.var b = new Function(
'a', //构造函数, 基本没人用
'b',
'return a+b'
);
b(1,2) //3

var b = Function('a','b','return a+b')
b(1,2) //3,加new和不加new没区别

var b = Function('return "hello world"');
//若只有一个参数,则就是函数体

4.var e = (a,b) => {
var m = a*2;
var n = b*2;
return m+n;
}

//若只有一句话,并且不返回对象,可以同时去掉return和{}
var e = (a,b) => a+b

//若只有一个参数,可以省略()
var e = n => n*n

2. 若重复声明函数,由于函数名的提升,前一次声明在任何时候都是无效的,这一点要特别注意。如下

function f() {
  console.log(1);
}
f() // 2

function f() {
  console.log(2);
}
f() // 2

函数的变量提升:

1
2
3
f();    //不会报错,因为下面的变量提升了

function f() {}

1
2
3
4
5
6
7
f();
var f = function (){};
// 报错,TypeError: undefined is not a function
// 因为上面这两行等同于下面
var f;
f();
f = function () {};

所以如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var f = function () {
console.log('1');
}

function f() {
console.log('2');
}

f() // 1,相当于下面

var f;
function f() {
console.log('2');
}
f = function () {
console.log('1');
}
f(); //1

3. 注意:不要在if语句和try语句里使用函数声明

1
2
3
4
5
6
7
8
9
10
11
12
if (false) {
function f() {} //即使是false,f函数也会被声 明,因为函数名的提升
}

f() // 不报错,因为函数名提升,所以不要在if 语句里使用函数声明

//但可以使用函数表达式
if (false) {
var f = function () {};
}

f() // undefined

4. 函数的属性

name

1
2
3
4
5
6
7
8
9
10
11
function f1() {}
f1.name // "f1",name属性

var f2 = function(){};
f2.name //f2

var f3 = function f4(){};
f3.name //f4

var f5 = new Function('x','y','return x+y');
f5.name //'anonymous',匿名的

length

1
2
function f(a, b) {}
f.length // 2,函数的参数个数

toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   function f(){
console.log('可以的');
}
f.toString(); // "function f(){
// console.log('可以的'); // }"
//利用这一点可以变相实现多行字符串
function f() {/*
这是一个
多行注释
*/}

f.toString()
// "function f(){/*
// 这是一个
// 多行注释
// */}"

变相实现多行字符串

5. 局部变量

在函数内部声明的变量是局部变量,在函数以外声明的全是全局变量,在其他区块中声明的一律也是全局变量。

6. 函数内部也有变量提升!

7. 函数的作用域,就是其声明时所在的作用域,与其运行时所在的作用域无关。

1
2
3
4
5
6
7
8
9
10
11
var a = 1;
var x = function () {
console.log(a);
};

function f() {
var a = 2;
x();
}

f() // 1

8. 传递方式

1
2
3
4
5
6
7
8
var obj = { p: 1 };

function f(o) {
o.p = 2;
}
f(obj); //相当于把obj的地址传进去

obj.p // 2

如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

1
2
3
4
5
6
7
8
var obj = [1, 2, 3];

function f(o) {
o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

这是因为,o的值实际是参数obj的地址,重新对o赋值导致o指向另一个地址,保存在原地址上的值不受影响。复合类型是传地址

1
2
3
4
5
6
7
8
var p = 2;

function f(p) {
p = 3;
}
f(p); //基本类型,所以传进去的不是地址而只是p的值

p // 2

原始类型的值,传入函数只是值的拷贝,所以怎么修改都不会影响到原始值

9. arguments对象

可以通过argument对象获得参数

1
2
3
4
5
function f(a, a) {
console.log(arguments[0]);
}

f(1) // 1

正常模式下,arguments对象还可以在运行时修改参数。严格模式下不行

1
2
3
4
5
6
7
8
9
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}

f(1, 1) // 5

arguments.length //可以知道传入多少个参数

arguments不是数组,但可以转换为数组

10. 返回值

函数即使没有return,JS会自动加上return undefined;

11. function的不一致性

1
2
3
4
5
function y(){};
y; //f y(){}

var x = function i(){};
i; //Error: i is not defined,i只能作用在它自己的函数里

12. 函数的调用

1
2
f(1,2);
f.call(undefined,1,2); //这两种调用方式等价

13. this和arguments

this是call()的第一个参数

1
2
3
4
5
6
7
8
9
10
f = function(){
console.log(this);
}
f.call(undefined); //正常模式下,若传入的是null或undefined,打印出来的就是window

f = function(){
'use strict'
console.log(this);
}
f.call(undefined); //严格模式打印出undefined

面试题1:

1
2
3
4
5
6
7
8
9
var obj = {
foo: function(){
console.log(this)
}
}

var bar = obj.foo
obj.foo() // 打印出obj,因为转换为 obj.foo.call(obj),this 就是 obj
bar() //打印出window,转换为 bar.call(),由于没有传东西,所以 this 就是 undefined,最后浏览器给你一个默认的 this —— window 对象

面试题2:

1
2
3
function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢?

我们可以把 arr0 想象为arr.0( ),虽然后者的语法错了,但是形式与转换代码一样,于是:

1
2
arr.0.call(arr);
//this就是arr

arguments是伪数组

1
2
3
4
f = function(){
console.log(arguments);
}
f.call(undefined,1,2,3); //[1,2,3,......]

arguments有数组的一些特性,但没有在Array的原型链中,所以arguments没有数组的方法,如push

1
arguments.__proto__ === Array.prototype //false

14. 函数的作用域

看到面试题先把代码改了,做变量提升!再做题

面试题1:

1
2
3
4
5
6
7
8
9
10
11
12
var a = 1;
function f1() {
console.log(a)
var a = 2;
f4.call();
}

function f4() {
console.log(a);
}

f1.call(); //??

答案:undefined(f1的console.log)、1(f4的console.log)

面试题2:

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = 1;
function f1() {
console.log(a)
var a = 2;
f4.call();
}

function f4() {
console.log(a);
}

a=2;
f1.call(); //?

答案:undefined、2

面试题3:

html

1
2
3
4
5
6
7
8
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>

JS

1
2
3
4
5
6
7
8
var liTags = document.querySelectorAll('li');
for(var i=0;i<liTags.length;i++){
liTags[i].onclick = function () {
console.log(i);
}
}

//请问点3时,输出什么

答案:6
先JS变量提升

1
2
3
4
5
6
7
8
var liTags;
var i;
liTags = document.querySelectorAll('li');
for(i=0;i<liTags.length;i++){
liTags[i].onclick = function () {
console.log(i);
}
}

因为点3时,for循环已执行完了,i变成6,所以输出的是6

面试题4

1
2
3
4
5
6
7
8
9
10
var x = function () {
console.log(a);
};

function y(f) {
var a = 2;
f();
}

y(x) // ?

答案:a is not defined
因为函数x在外部,不会引用y的内部变量

15. 闭包

1
2
3
4
5
var a = 1;

function f4() {
console.log(a);
}

若一个函数使用了它作用域外的变量,那 (这个函数+变量) 就是闭包。
函数外部无法访问函数内部的变量,但使用闭包就可以

1
2
3
4
5
6
7
8
9
10
function f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}

var result = f1();
result(); // 999,闭包就是f2

本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的第二个作用就是让它读取的这些变量始终保持在内存中

1
2
3
4
5
6
7
8
9
10
11
function createIncrementor(start) {
return function () {
return start++;
};
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

闭包的另一个用处,是封装对象的私有属性和私有方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}

return {
name: name,
getAge: getAge,
setAge: setAge
};
}

var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25

函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。

16. 同名参数

1
2
3
4
5
6
7
8
9
10
11
function f(a, a) {
console.log(a);
}

f(1, 2) // 2,以后面那个为准

function f(a, a) {
console.log(a);
}

f(1) // undefined,以后面那个为准,若要取第一个,可以用arguments对象

17 .立即调用的函数表达式(IIFE)

1
2
function(){ /* code */ }();
// SyntaxError: Unexpected token (

因为JS把function在行首的,一律解释成语句,而语句后不应该出现(),最简单的处理,就是将其放在一个圆括号里面。

1
2
3
4
5
6
7
8
9
10
11
12
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
//或者
-function(){ /* code */ }();
//或者
+function(){ /* code */ }();
//或者
!function(){ /* code */ }();
//或者
~function(){ /* code */ }();
//注意,上面前两种写法最后的分号都是必须的。

推而广之,任何以表达式来处理函数定义的方法,都是可以的

18. eval

eval命令的作用是,将字符串当作语句执行。

1
2
eval('var a = 1;');
a // 1

JavaScript 规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域。

1
2
3
4
5
(function f() {
'use strict';
eval('var foo = 123');
console.log(foo); // ReferenceError: foo is not defined
})()

即使在严格模式下,eval依然可以读写当前作用域的变量。

1
2
3
4
5
6
(function f() {
'use strict';
var foo = 1;
eval('foo = 2');
console.log(foo); // 2
})()

JavaScript Dom入门

Posted on 2018-08-16

DOM 目前的通用版本是DOM3

1. JS初级主要就两个作用:

1.找元素
2.给元素增加/删除class

2. 节点API

  • 1. nodeType

    1
    2
    3
    4
    5
    6
    7
    8
    Node.ELEMENT_NODE === 1  //true,元素节点
    Node.TEXT_NODE === 3 //true,文本节点
    Node.COMMENT_NODE === 8 //true,注释节点
    Node.DOCUMENT_TYPE_NODE === 10 //true,例:<!DOCTYPE html>
    Node.DOCUMENT_FRAGMENT_NODE === 11
    //重点!

    xxx.nodeType === 3 //可用来检验xxx是一个元素节点还是元素中的文本
  • 2. tagName
    ul的tagName为’UL’(大写!)

    1
    let brother = li.getElementsByTagName('UL')[0];

    getElementsByTagName这个API搜索的是li元素的后代,并按顺序返回所有的ul,返回的是一个数组,要写上[0]

  • 3. getAttribute

    1
    2
    a.href     //这样获取href,浏览器会给href加上http协议
    a.getAttribute('href') //这样获取的才是href本身
  • 4. target和currentTarget

    1
    2
    3
    4
    liTags[i].onmouseenter = function (x) {
    console.log(x.target); //x.target是我们操作的那个元素,若a里包含了个span,那操作的就是span
    console.log(x.currentTarget) //x.currentTarget是我们监听的元素,就是liTags[i]这个元素
    }
  • 5. 选择器

    1
    2
    let a = document.querySelector('a[href="' + id + '"]')
    //href外要包一个中括号,地址外要包一个双引号!并且变量id要和其他的用加号隔开
  • 6. 儿子children

    1
    2
    var c = div.children;    //会获得div下的所有子标签
    var c = div.childNodes; //会获得div下的所有子标签加文本标签
  • 7. Siblings
    获取所有兄弟方法一:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var getSiblings = function (elem) {
    var siblings = [];
    var sibling = elem.parentNode.firstChild;
    for (; sibling; sibling = sibling.nextSibling) {
    if (sibling.nodeType !== 1 || sibling === elem) continue;
    siblings.push(sibling);
    }
    return siblings;
    };

获取所有兄弟方法二 用getSiblings():

1
2
var elem = document.querySelector('#some-element');
var siblings = getSiblings(elem);

注意:nextSibling如果有换行,会把换行当作下一个兄弟

  • 8. nodeName()

    1
    2
    document.body.nodeName    //'BODY',body的节点名字,(除了svg,其他节点名字返回都是大写)
    document.documentElement.nodeName //'HTML'
  • 9. cloneNode()

    1
    2
    var div2 = div1.cloneNode();  //会把div1本身复制给div2
    var div2 = div1.cloneNode(true); //会把div1及它的所有子节点一起复制给div2,这句等于var div2=div1;
  • 10. isSameNode()和isEqualNode()
    isEqualNode检测内容是否相同
    isSameNode检测两个元素是否是同一个节点

  • 11. removeChild()
    在页面里移除child节点,但是child在内存还是存在的
  • 12. replaceChild()
    替换儿子,被替换的只是在页面里不见了,还是存在内存里的
  • 13. normalize()
    将代码正常化
  • 14. innerText()和textContent()

    innerText是IE的私有实现,但也被除FF之外的浏览器所实现,textContent 则是w3c的标准API,现在IE9也实现了。

    区别1:textContent会获取所有元素的内容,包括script和style标签里面的内容,innerText不会
    区别2:innerText不会返回隐藏元素的文本(像display:none),但textContent会
    区别3:innerText会受CSS的影响,触发重排,而textContent不会,所以textContent快
    但用这两个API赋值的时候,都会覆盖掉元素里原有的内容

    1
    2
     div1.textContent = 'hello';  //这样div1里的东西都会被hello覆盖掉
    div1.appendChild(document.createTextNode('hello')); //这样就不会覆盖掉div1里的东西

3. Document API

  • 1.document.children
    [html],document是html的爸爸

  • 2.document.documentElement
    就是html

  • 3.document.write
    尽量不用,若用在延时性和异步性的方法里,会把整个页面的内容覆盖掉

  • 4.querySelector()和querySelectorAll()
    querySelectorAll返回的是一个伪数组

4. 用JS完成页面内的跳转

1
2
3
4
5
6
7
8
9
10
11
let aTags = document.querySelectorAll('nav.menu > ul > li > a');   //获取所有a标签,返回的是一个数组
for(let i=0;i<aTags.length;i++) { //因为aTags是数组,所以要用for循环,同时监听到数组里的所有元素
aTags[i].onclick = function (x) { //监听a标签被鼠标点击的动作
x.preventDefault(); //防止默认动作(跳转)
let a = x.currentTarget; //当前监听的元素a
let href = a.getAttribute('href'); //获取a的href值
let element = document.querySelector(href); //获取带有这个href值的标签 例:'#siteAbout'
let top = element.offsetTop; //获取这个标签的底端到整个页面顶端的距离(固定值)
window.scrollTo(0,top); //跳转window.scrollTo(x,y);
}
}

5. console.log调试大法

出问题了就在每一步console.log,能解决很多问题

6. setInterval()

1
2
3
4
5
6
7
8
9
let i=0;
let say = setInterval(() => {
if(i === 25){
window.clearInterval(say); //调用setInterval的名字来停止它
return;
}
i++;
console.log(i); //从1报数到25,一秒一次,到25就停
},1000);

7. Tween.js

缓动动画速查表

  1. 在cdn里搜tween,并在html中引用
  2. 在github-tween中查看使用教程

Js内存简介

Posted on 2018-08-12

内存

下面介绍浏览器里js的内存

  1. 除了对象类型,其他类型的数据是直接存在stack里

  2. 存对象的时候,将数据存在Heap(堆内存)里,并将数据在Heap中的位置信息存在stack(栈内存)里,由于Heap里存数据不是按顺序存的,所以很方便对象随时添加属性。当把对象赋给另一个对象时,是把这个对象的地址赋给它。
    所以用对象时是引用的对象的地址,这种关系叫引用

  3. 8g的内存相当于 8 1024 1024 1024 8 = 6.87E10 bit
    而64位二进制的数相当于 2^64^ = 1.84E19,所以64位二进制能用来存内存里的所有位置

  4. 数字是64位的,字符是16位的(ES3)

5. 面试题

1.引用自身

错误实例:

1
2
3
4
5
6
var a = {self : a};     //这样a.self会是undefined

上面这样等于
var a; //undefined
a.self = a //这时a还是是undefined
因为赋值时要先确定右边的

正确写法:

1
2
var a = {};
a.self = a; //这样a.self就等于它自己

2
面试2.png

1
2
3
4
5
6
var a = {n:1};
var b = a;
a.x = a = {n:2} //这一行运行的时候最左的a地址已经确定为最开始的地址,于是先运行a = {n:2},相当于给了a一个新地址,再运行a.x = a,相当于把新地址赋给了旧地址上对象一个x属性

alert(a.x); // undefined,然而有了新地址的a并没有x属性
alert(b.x); // [object,Object],b是引用的a的旧地址,所以b有

3. 垃圾、垃圾回收

1
2
3
4
var a = function(){};
var a = null;

这样上面那个function因为没有被任何东西引用,所以function占的内存就是垃圾,会被浏览器回收

下面是面试题

1
2
3
var fn = function(){};
document.body.onclick = fn;
fn = null;

请问function是垃圾吗?
答案不是,因为document.body.onclick = fn;还在引用
面试3

想要function变成垃圾可写
document.body.onclick = null;
附加:若是把页面关了,就相当于document不存在了,于是这时body、onclick、function都是垃圾,浏览器会把它们回收,但IE6不会把它们当作垃圾,IE6有bug!这就叫内存泄露,垃圾会占满内存,造成卡顿,解决办法:

1
2
3
window.onunload = function(){
document.body.onclick= null;
}

JavaScript原型

Posted on 2018-08-09

原型

学习Javascript之初总是被proto 与 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var num = 1
num.__proto__ === Number.prototype
num.__proto__.__proto__ === Object.prototype
num.__proto__.constructor === Number
Number.prototype.__proto__ === Object.prototype
Number.prototype.constructor === Number

var fn = function(){}
fn.__proto__ === Function.prototype
fn.__proto__.__proto__ === Object.prototype
fn.__proto__.constructor === Function
Function.prototype.__proto__ === Object.prototype
Function.prototype.constructor === Function

var array = []
array.__proto__ === Array.prototype
array.__proto__.__proto__ === Object.prototype
array.__proto__.constructor === Array
Array.prototype.__proto__ === Object.prototype
Array.prototype.constructor === Array

var bool = true
bool.__proto__ === Boolean.prototype
bool.__proto__.__proto__ === Object.prototype
bool.__proto__.constructor === Boolean/**/
Boolean.prototype.__proto__ === Object.prototype
Boolean.prototype.constructor === Boolean

var str = "String"
str.__proto__ === String.prototype
str.__proto__.__proto__ === Object.prototype
str.__proto__.constructor === String
String.prototype.__proto__ === Object.prototype
String.prototype.constructor === String

var object = {}
object.__proto__ === Object.prototype
object.__proto__.__proto__ === null
object.__proto__.constructor === Object
Object.prototype.__proto__ === null
Object.prototype.constructor === Object

运行都为true,从以上代码一步步讲解实例,原型,原型链,构造函数,构造函数属性,new指令 及 js一切皆对象的误解

1.构造函数

JS中以上代码会用到的几个构造函数分别是

1
Function,Number,Boolean,String,Object

而这些构造函数都是由 Function构造出来的, 所以

1
2
3
4
5
6
Function.__proto__ === Function.prototype // 为 true
Array.__proto__ === Function.prototype // 为 true
Object.__proto__ === Function.prototype // 为 true
Number.__proto__ === Function.prototype
Boolean.__proto__ === Function.prototype
String.__proto__ === Function.prototype

以上第一句代码 var num = 1 其实应该这样写 var num = new Number(1) , 那么为什么 直接赋值num还是可以使用很多Number的方法呢

因为JS在使用num的时候会创建一个临时的Number(1)给num ,然后num就可以正常使用Number的方法,本质上num还是基础数据类型,而不是混合数据类型的对象形式
那么为什么要使用new呢
当一个函数New的时候 会执行以下几步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//使用new 会有以下四个步骤
function fn(){
1. 创建一个临时对象
var temp = {}

2. 临时对象__proto__ = fn.原型
temp.__proto__ = fn.prototype

3. 返回这个临时对象
return temp
}

fn.prototype = {
constructor : fn
}

那么执行完 var num = new Number(1) 后 num就是一个对象了, 而这个对象里面有一个proto的属性 。

2. 实例

所以实例就是: 执行构造函数后得到的这个对象

3. 原型

而原型就是: 实例proto属性指向的 该函数的 prototype属性也是一个对象

4. 构造函数属性

构造函数属性constructor指向该构造函数本身

5. 原型链

而 原型链就是 num.proto.proto.proto === null 这条链所对应的值

6. 读取属性

当我们读取属性时, 从实例对象的属性开始找,如果实例属性中没有需要的属性,就去原型中找,如果该原型中还是没有,又会继续往下一层原型中找,直到顶层为止。 num.proto.proto.proto === null只想null的时候

7. 总结

所有的构造函数都是由Function构造函数所构造出来的

1
所有的实例对象的```__proto__```属性都指向该构造函数的```prototype属性array.__proto__ === Array.prototype

所有的实例对象的

1
所有的原型的```constructor```属性都指向该构造函数```num.__proto__.constructor === Number

所有Number.prototype.__proto__ === Object.prototype
Object的实例对象的__proto__.__proto__为null

1234

梁光静

36 posts
10 tags
© 2019 梁光静
Powered by Hexo
|
Theme — NexT.Muse v5.1.4