My Little World

learn and share


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于
My Little World

变量提升

发表于 2017-08-09

一个变量的生成需要经历 创建、初始化、赋值三个阶段
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
let
1、
假设有如下代码:

function fn(){
var x = 1
var y = 2
}
fn()

在执行 fn 时,会有以下过程(不完全):

进入 fn,为 fn 创建一个环境。
找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
将这些变量「初始化」为 undefined。
开始执行代码
x = 1 将 x 变量「赋值」为 1
y = 2 将 y 变量「赋值」为 2
也就是说 var 声明会在代码执行之前就将「创建变量,并将其初始化为 undefined」。

这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined。

2、
fn2()

function fn2(){
console.log(2)
}
JS 引擎会有一下过程:

找到所有用 function 声明的变量,在环境中「创建」这些变量。
将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
开始执行代码 fn2()
也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」
3、
{
let x = 1
x = 2
}
只看 {} 里面的过程:

找到所有用 let 声明的变量,在环境中「创建」这些变量
开始执行代码(注意现在还没有初始化)
执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
执行 x = 2,对 x 进行「赋值」
这就解释了为什么在 let x 之前使用 x 会报错:

let x = ‘global’
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
原因有两个

console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的暂时死区)

补充
1.
在进入一个执行环境后,先把 var 和 function 声明的变量前置, 再去顺序执行代码
是 var 声明在前还是 function 声明的在前?按先来后到,同名覆盖。当然如果一个变量已经有值,再 var 是无效的

1
2
3
4
5
6
7
8
9
10

var fn
function fn(){}

console.log(fn) //function

function fn(){}
var fn //已经声明过 fn, 再 var 无效,并不会重置为 undefined

console.log(fn) //function

2.
函数在执行的过程中,先从自己内部找变量
如果找不到,再从创建当前函数所在的作用域去找, 以此往上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
var a = 2
return fn2
}
var fn = fn1()
fn() //输出多少 2
////////////////////////////////
var a = 1
function fn1(){
var a = 2
return fn2
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() //输出多少 1

相关

1
2
3
4
5
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a)//{n:2}
console.log(b)//{n:1,x:{n:2}}

a.x = a = {n:2};执行顺序

  1. a.x,这时a,b指向{n:1},增加x,属性,但值为undefined,即这时a,b为{n:1,x:undefined}

2.a={n:2},a这时指向{n:2},b依然为{n:1,x:undefined}

3.a.x = a;这时的x是{n:1,x:undefined}中的x,指向a,此时a指向{n:2}

1
a==1 && a==2 && a==3

可能为true
关键在于==在比较两端之前会调用对象的valueof,toString等方法,只要a对象中的toString方法有递增值返回就可以实现

1
2
3
4
5
6
const a = {
i: 1,
toString: function () {
return a.i++;
}
}

立即执行函数的目的
造出一个函数作用域,防止污染全局变量
ES 6 新语法

1
2
3
{
let name
}

My Little World

关于this

发表于 2017-08-09

普通函数中的this

  1. this总是代表它的直接调用者, 例如 obj.func ,那么func中的this就是obj
    2.在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window
    3.在严格模式下,没有直接调用者的函数中的this是 undefined
    4.使用call,apply,bind绑定的,this指的是 绑定的对象,
    bind绑定一次后不会改变,且bind 的执行的结果返回的是绑定了一个对象的新函数
    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
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    1、
    function test() {
    console.log(this);//window
    }
    test();

    2、
    function test() {
    'use strict';
    console.log(this);//undefined
    }
    test();

    3、
    window.val = 1;
    var obj = {
    val: 2,
    fn: function () {
    this.val *= 2; //普通函数,this指向调用者
    val *= 2;
    console.log(this.val);
    console.log(val);
    }
    };
    obj.fn();// 4 2
    var func = obj.fn;
    func(); //8 8
    //obj.fn()执行时,val 没有在fn的作用域里面定义,则去obj.fn()的作用域里面找,obj.fn()位于window,window.val是1;this指向obj,this.val是2
    //func()执行时,window.val由于执行obj.fn(),现在是2;func()在window作用域下执行,this就是window,所以this.val和val都是window.val

    4、
    function f() {
    var test = 'in the f!';
    setTimeout(function(){ //是函数就会建立作用域
    console('inner '+ test) // inner in the f!
    }, 0);
    }
    //以上代码等于
    function f() {
    var test = 'in the f!';

    function ff() {
    alert('inner ' + test) //test在ff里面没定义,但在f里面进行了定义
    } // 能访问到f中的test局部变量

    setTimeout(ff, 0); // inner in the f!
    }

    f();
    5、
    var lzh = {
    name: 'lzh',
    say: function(something){
    alert(something + this.name);
    }
    }

    var iny = {
    name: 'iny'
    }

    lzh.say.apply(iny, ['hi, I am ']); // 输出 hi I am iny
    6、
    var arr = []
    for(var i=0; i<3; i++){
    arr[i] = function(){ console.log(this) }
    }
    var fn = arr[0]

    arr[0]
    /*
    解析: 因为函数是个特殊的对象,所以 arr 相当于 { '0': function(){}, '1': function(){}, '2': function(){}, length:3}
    arr[0]相当于 `arr['0']` 相当于 `arr.0` (当然这种写法不符合规范),所以 arr[0]等价于 arr.0.call(arr), this就是 arr
    */

    fn()
    /*
    解析: 相当于 `fn.call(undefined)`, 所以 fn 里面的 this 是 Window
    */
    7、
    var app = {
    container: document.querySelector('body'),
    bind: function(){
    this.container.addEventListener('click', this.sayHello) //点击的时候会执行 sayHello,sayHello 里面的 this 代表 body 对象
    this.container.addEventListener('click', this.sayHello.bind(this)) //点击的时候会执行 sayHello,sayHello 里面的 this 代表 app 对象
    },
    sayHello: function(){
    console.log(this)
    }
    }
    app.bind()

箭头函数中的this

默认指向在定义它时,它所绑定的对象的上一级,而不是执行时的对象, 定义它的时候,可能环境是window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {
return () => {
return () => {
return () => {
console.log(this);
};
};
};
}
foo()()()() //window
var f = foo.call({id: 1});
f()()() //{id:1}
var t1 = f.call({id: 2})()();//{id:1}
var t2 = f().call({id: 3})();//{id:1}
var t3 = f()().call({id: 4});//{id:1}

setTimeout中的this

普通函数指向window
箭头函数指向定义的对象
引用函数,指向调用对象

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
81
82
83
 0、
var obj = {
fn1 : function () {
console.log(this); //obj
},
fn2 : function () {
setTimeout(function () {
console.log(this); //window 匿名函数执行在window环境,找不到宿主对象,所以指向window
},0);
},
fn3: function () {
setTimeout(() => {
console.log(this); //obj 箭头函数创建在obj上
},100);
}
fn4: function () {
var that = this;
setTimeout(function () {
console.log(that) //obj 在setTimeout里面引用obj
console.log(this) //window
});
}
fn5: function () {
var f1 = () => {
console.log(this); // obj f1定义处在obj里面
setTimeout(() => {
console.log(this); // obj 箭头函数定义处在obj里面
})
}
f1();
}
fn6: function () {
var f2 = function () {
console.log(this); // window, f2调用时,没有宿主对象,默认是window
setTimeout(() => {
console.log(this); // window 箭头函数定义在f2确定的window里面
})
};
f2();
}
fn7: function () {
'use strict';
var f3 = function () {
console.log(this); // undefined
setTimeout(() => {
console.log(this); // undefined
})
};
f3();
}
};
obj.fn1();
obj.fn2();
obj.fn3();
obj.fn4();
···

1、
function foo(){
setTimeout(function(){
console.log(this) //window 匿名函数,找不到宿主对象
}, 100);
setTimeout(() => {
console.log(this) //window foo挂载在window对象上
}, 100);
}
foo();
foo.call({id:42}); //window {id:42} foo运行时所在的对象,恰好是箭头函数定义时所在的对象
//call([thisObj[,arg1[, arg2,....]) 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

2、
function method() {
alert(this.value); // 输出 42 第二个this
}

function Foo() {
this.value = 42;
setTimeout(this.method, 500); // 这里this指向window 第一个this
}

Foo();
//Foo挂载在window上,当执行Foo时,this指向window,Foo里面的value被挂到window,method本来就挂在window上,所以执行this.method就是调用window.method
//method执行,它的this指向window,这时window已经挂上value值42
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
81
82
83
84
85
86
87
 //其他理解
let app = {
fn1: function(a){
console.log(this) //app
}
fn2(a) {
consoel.log(this) //app
},
fn3: (a)=>{
console.log(this) //window
}
}

app.fn2.call(app)
app.fn3.call( 它的上一级的 this )
1.
var app = {
init() {
var menu = {
init: ()=>{
console.log(this)
},
bind() {
console.log(this)
}
}
menu.init()
/*相当于 menu.init.call(menu 所在的环境下的 this) , 所以 init 里面的 this 也就是 app。
(假设 app.init 也是箭头函数,想想 menu.init 里面的 this 是什么?)
*/
menu.bind()
/*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
*/
}
}
app.init()


2.
var app = {
fn1() {
setTimeout(function(){
console.log(this)
}, 10)
},
fn2() {
setTimeout(()=>{
console.log(this)
},20)
},
fn3() {
setTimeout((function(){
console.log(this)
}).bind(this), 30)
},
fn4: ()=> {
setTimeout(()=>{
console.log(this)
},40)
}
}
app.fn1()
app.fn2()
app.fn3()
app.fn4()

var app = {
fn1() {
function fn(){
console.log(this)
}
//过10ms 后执行
//fn.call(undefined) ,所以输出 Window
},
fn2() {
//过20ms 执行箭头函数
//箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app
},
fn3() {
// 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app
// 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app
}
fn4: ()=> {
//过40ms 执行箭头函数
//箭头函数里面没资格有 this,用 setTimeout 外面的 this
//setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,也就是 Window
}

应用

My Little World

Travish CI使用

发表于 2017-08-08

Travish CI使用

1、使用github 账号登陆https://www.travis-ci.org/
2、点击右上角自己的账户名,选择accounts,弹出在github上所有的仓库
ci1.png
点击这个按钮重新从github上获取所有仓库
ci2.png
3、点击对应仓库按钮,变为对勾,则开启对应仓库的集成化测试,每次提交代码到远程仓库都进行一次测试
4、点击仓库名,可进入到测试界面,
ci3.png
5、为项目添加.travis.yml文件
.travis.yml 文件编写的相关语法可参考https://docs.travis-ci.com/user/languages/javascript-with-nodejs/

.travis.yml 文件可以在https://lint.travis-ci.org/页面进行检查,是否编写正确

6、push代码,在Travish CI等待测试结果
页面图标因为有缓存所以可能出现不能及时更换状态的情况,这个注意看结果就好

My Little World

fork仓库

发表于 2017-08-08

fork

在页面点击Fork,将被人的仓库复制到自己的远程仓库
使用git clone 将远程仓库克隆到本地
切换分支到develop git checkout -b develop
修改完代码后
git add . && git commit -m ‘test new pull request’
git push origin develop
然后在github页面找到刚刚fork的仓库,切换branch到develop,
点击New pull request进行merge

保持与原仓库同步

git remote -v
查看当前的远程仓库地址,输出如下:

origin https://github.com/YooHannah/homework2.git (fetch)
origin https://github.com/YooHannah/homework2.git (push)
可以看到从自己帐号 clone 下来的仓库,远程仓库地址是与自己的远程仓库绑定的(这不是废话吗)

接下来运行

git remote add upstream https://github.com/FE-star/homework2.git
这条命令就算添加一个别名为 upstream(上游)的地址,指向之前 fork 的原仓库地址。git remote -v 输出如下:

origin https://github.com/YooHannah/homework2.git (fetch)
origin https://github.com/YooHannah/homework2.git (push)
upstream https://github.com/FE-star/homework2.git (fetch)
upstream https://github.com/FE-star/homework2.git (push)
之后运行下面几条命令,就可以保持本地仓库和上游仓库同步了

git fetch upstream
git checkout master 如果本来就在master上就不用切换
git merge upstream/master
接着就是熟悉的推送本地仓库到远程仓库

git push origin master

Git 合并两个不同的仓库

My Little World

事件冒泡和事件捕获

发表于 2017-08-06

addEventListener/attachEvent/element.onclick

辨别区分addEventListener、attachEvent和element.onclick

attachEvent事件

attachEvent是ie添加事件处理程序,接收两个参数,其中事件类型名称要加”on”,
可以添加多个事件处理程序,按照添加顺序相反的顺序触发

addEventListener事件

addEventListener是给非ie添加事件处理程序,接收三个参数,第一个是事件名,不需要加“on”,
第二个是绑定的函数,第三个参数是一个布尔值,如果是false,就使用传统的冒泡方式,
如果为true,就在捕获阶段调用事件处理程序
可以添加多个事件处理程序,按照添加顺序触发

onclick

el.onclick相当于在标签上写onclick

区别

1.上下文
attachEvent会在全局作用域中运行,this等于window对象
addEventLinstener在其依附的元素的作用域中运行,this等于绑定元素对象
使this关键字都指向元素处理方法

1
2
3
4
5
6
function bind(el, fn, type){
var _fn = function(){
fn.apply(el, arguments);
};
window.addEventListener ? el.addEventListener(type, _fn, false) : el.attachEvent("on" + type, _fn);
}

2.绑定
el.onclick通过标签的onclick属性输入到文档,然后由文档解析成事件
attachEvent和addEventLinstener要在文档解析完成以后,通过文档的dom接口去绑定的事件
资源加载和页面事件

3.取消绑定
el.onclick:el.onclick=null;
addEventListener:removeEventListener();
attachEvent():detachEvent()

4.获取event事件
非IE:

1
2
3
4
5
6
el.onclick=function(event){
  alert(event.type); //"click"
};
el.addEventListener("click",function(event){
  alert(event.type); //"click"
},false);

IE:
通过el.onclick绑定的事件处理程序中,event对象作为window对象的一个属性存在。
el.onclick=function(){
  var event=window.event;
  alert(event.type); //“click”
}
如果通过attachEvent()添加事件处理程序时,event对象作为参数被传入事件处理程序,
el.attachEvent(“onclick”,function(event){
  alert(event.type); //“click”
});

标签时一样

1
<input type="button" value="Click me" onclick="alert(event.type)"/>   //"click"

事件捕获、事件冒泡

二者是指当事件在某一DOM元素被触发时,该DOM元素的父级元素也绑定了触发事件,则触发事件的执行顺序

事件冒泡

事件自下而上依次执行,先执行子元素触发事件,再执行父元素触发事件,
addEventListener第三个参数设置为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
<body>
<div id="parent">
  <div id="child" class="child">您好</div>
</div>
</body>

<script type="text/javascript">
document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+this.id);
})
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+this.id)
})
document.getElementById("parent").onclick=function(e){
alert("parentonclik事件被触发,"+this.id);
}
document.getElementById("child").onclick=function(e){
alert("childonclik事件被触发,"+this.id);
}
</script>
//点击'您好',弹出顺序:child事件被触发->childonclik事件被触发->parent事件被触发->parentonclik事件被触发
<script type="text/javascript">
document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+this.id);
},false)
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+this.id)
},true)
</script>
//点击'您好',先弹出child事件被触发,再弹出parent事件被触发

事件捕获

事件从上到下一次执行,先执行父元素触发事件,再执行子元素触发事件,
addEventListener第三个参数设置为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
<body>
<div id="parent">
  <div id="child" class="child">您好</div>
</div>
</body>

<script type="text/javascript">
document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+this.id);
},true)
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+this.id)
},true)
document.getElementById("parent").onclick=function(e){
alert("parentonclik事件被触发,"+this.id);
}
document.getElementById("child").onclick=function(e){
alert("childonclik事件被触发,"+this.id);
}
</script>
//点击'您好',弹出顺序:parent事件被触发->child事件被触发->childonclik事件被触发->parentonclik事件被触发
<script type="text/javascript">
document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+this.id);
},true)
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+this.id)
},false)
</script>

//点击'您好',先弹出parent事件被触发,再弹出child事件被触发

应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//要求:鼠标放到li上对应的li背景变灰
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
<li>item6</li>
</ul>
//实现一:利用事件冒泡实现,不用遍历所有li节点
$("ul").on("mouseover",function(e){
$(e.target).css("background-color","#ddd").siblings().css("background-color","white");
})
//实现二:给每个li绑定事件,缺点,动态的加载了一些元素,新增li后,还要再绑定一次事件
$("li").on("mouseover",function(){
$(this).css("background-color","#ddd").siblings().css("background-color","white");
})

禁止冒泡

1
2
3
4
5
6
7
8
9
function doSomething(e) {
if (!e) {//微软模型
var e = window.event;
e.cancelBubble = true;
}
if (e.stopPropagation) {//w3c事件模型
e.stopPropagation();
}
}

补充

事件委托是什么?有什么好处?

假设父元素有4个儿子,我不监听4个儿子,而是监听父元素,看触发事件的元素是哪个儿子,这就是事件委托。
可以监听还没有出生的儿子(动态生成的元素)。省监听器。

My Little World

关于Performance API

发表于 2017-08-03

load/unload事件

load事件

当页面完全加载后(包括所有图像、JavaScript文件、CSS文件等外部资源),就会触发window上面的load事件
使用方法

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
//方法一
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
}
}
EventUtil.addHandler(window,"load",function(event){
alert("loaded!");
});
//方法二
<!DOCTYPE html>
<html>
<head>
<title>load Event Example</title>
</head>
<body onload = "alert("Loaded!")">
</body>
</html>

document.ready 监控dom是否加载完毕,dom加载完毕时及资源加载之前触发
DOMContentLoaded 当页面的DOM树解析好并且需要等待js执行完才触发

unload事件

在文档被完全卸载后触发,只要用户切换到另一个页面,就会发生unload事件。
利用这个事件最多的情况是清除引用,以避免内存泄漏
使用方法

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
//方法一
var EventUtil = {
getEvent: function (event) {
return event ? event : window.event;
},
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
};
EventUtil.addHandler(window, "unload", function (event) {
alert("Unloaded");
})
//方法二
<html>
<head>
<tilte>卸载(unload)事件</tile>
</head>
<body onunload="alert('Unload')">
</body>
</html>

Performance API

Performance 接口会给出某个页面的与时间相关的性能信息
使用方法为 window.performance.属性/方法

Performance API用于精确度量、控制、增强浏览器的性能表现。这个API为测量网站性能,提供以前没有办法做到的精度
目前,所有主要浏览器都已经支持performance对象,包括Chrome 20+、Firefox 15+、IE 10+、Opera 15+。

performance.timing

包含各种与浏览器性能有关的时间数据,提供浏览器处理网页各个阶段的耗时

  • navigationStart:当前浏览器窗口的前一个网页关闭,发生unload事件时的Unix毫秒时间戳。如果没有前一个网页,则等于fetchStart属性。即浏览器处理当前网页的启动时间
  • unloadEventStart:如果前一个网页与当前网页属于同一个域名,则返回前一个网页的unload事件发生时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0。
  • unloadEventEnd:如果前一个网页与当前网页属于同一个域名,则返回前一个网页unload事件的回调函数结束时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0。
  • redirectStart:返回第一个HTTP跳转开始时的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0。
  • redirectEnd:返回最后一个HTTP跳转结束时(即跳转回应的最后一个字节接受完成时)的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0。
  • fetchStart:返回浏览器准备使用HTTP请求读取文档时的Unix毫秒时间戳。该事件在网页查询本地缓存之前发生。
  • domainLookupStart:返回域名查询开始时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。
  • domainLookupEnd:返回域名查询结束时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。
  • connectStart:返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。
  • connectEnd:返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。
  • secureConnectionStart:返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。
  • requestStart:返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。
  • responseStart:返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。
  • responseEnd:返回浏览器从服务器收到(或从本地缓存读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
  • domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的readystatechange事件触发时)的Unix毫秒时间戳。
  • domInteractive:返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。
  • domContentLoadedEventStart:返回当前网页DOMContentLoaded事件发生时(即DOM结构解析完毕、所有脚本开始运行时)的Unix毫秒时间戳。
  • domContentLoadedEventEnd:返回当前网页所有需要执行的脚本执行完成时的Unix毫秒时间戳。
  • domComplete:返回当前网页DOM结构生成时(即Document.readyState属性变为“complete”,以及相应的readystatechange事件发生时)的Unix毫秒时间戳。
  • loadEventStart:返回当前网页load事件的回调函数开始时的Unix毫秒时间戳。如果该事件还没有发生,返回0。
  • loadEventEnd:返回当前网页load事件的回调函数运行结束时的Unix毫秒时间戳。如果该事件还没有发生,返回0。

unloadEventStart 不等于navigationStart,navigationStart比unloadEventStart早
domComplete 等于 loadEventStart

performance.now()

performance.now方法返回当前网页自从performance.timing.navigationStart到当前时间之间的微秒数(毫秒的千分之一)。

performance.mark()

mark方法用于为相应的视点做标记。
window.performance.mark(‘mark_fully_loaded’);
clearMarks方法用于清除标记,如果不加参数,就表示清除所有标记。
window.peformance.clearMarks(‘mark_fully_loaded’);
window.performance.clearMarks();

performance.getEntries()

浏览器获取网页时,会对网页中每一个对象(脚本文件、样式表、图片文件等等)发出一个HTTP请求。performance.getEntries方法以数组形式,返回这些请求的时间统计信息,有多少个请求,返回数组就会有多少个成员.
由于该方法与浏览器处理网页的过程相关,所以只能在浏览器中使用。
window.performance.getEntries()[0] //获取第一个HTTP请求(即网页的HTML源码)的时间统计信息

performance.navigation对象

1、performance.navigation.type
该属性返回一个整数值,表示网页的加载来源,可能有以下4种情况:

  • 0:网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载,相当于常数performance.navigation.TYPE_NAVIGATENEXT。
  • 1:网页通过“重新加载”按钮或者location.reload()方法加载,相当于常数performance.navigation.TYPE_RELOAD。
  • 2:网页通过“前进”或“后退”按钮加载,相当于常数performance.navigation.TYPE_BACK_FORWARD。
  • 255:任何其他来源的加载,相当于常数performance.navigation.TYPE_UNDEFINED。

2、performance.navigation.redirectCount
该属性表示当前网页经过了多少次重定向跳转.
参考资料

页面请求加载过程

var t = performance.timing;

1、首先,在浏览器地址栏中输入url

2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。

3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。
var dns = t.domainLookupEnd - t.domainLookupStart; //域名解析时间
4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。
var tcp = t.connectEnd - t.connectStart;//浏览器与服务器之间的连接建立时间
5、握手成功后,浏览器向服务器发送http请求,请求数据包。
t.requestStart 浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的时间
6、服务器处理收到的请求,将数据返回至浏览器

7、浏览器收到HTTP响应
var ttfb = t.responseStart - t.navigationStart;//读取页面第一个字节之前的耗时
8、读取页面内容,浏览器渲染,解析html源码
var getdata = t.responseEnd-t.responseStart;//接收数据时间
9、生成Dom树、解析css样式、js交互
var dom = t.domInteractive-t.domLoading; //从构建DOM到页面与用户可以开始交互的时间
var script= t.domContentLoadedEventEnd-t.domContentLoadedEventStart;//脚本运行时间
var load = t.loadEventEnd-t.loadEventStart;//load事件处理时间

10、客户端和服务器交互

11、ajax查询

步骤二详细步骤

  • 浏览器缓存:浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求;
  • 操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统,获取操作系统的记录(保存最近的DNS查询缓存);
  • 路由器缓存:如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;
  • ISP缓存:若上述均失败,继续向ISP搜索。

浏览器渲染步骤

  • 解析html以构建dom树 分词器器、解析器
  • 构建render树
  • 布局render树
  • 绘制render树

渲染引擎开始解析html,并将标签转化为内容树中的dom节点。接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树。

Render树由一些包含有颜色和大小等属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。

Render树构建好了之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标。再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点

现代浏览器工作原理

Navigator 对象

包含有关浏览器的信息。
appCodeName 返回浏览器的代码名
appName 返回浏览器的名称
appVersion 返回浏览器的平台和版本信息
cookieEnabled 返回指明浏览器中是否启用 cookie 的布尔值
platform 返回运行浏览器的操作系统平台
userAgent 返回由客户机发送服务器的user-agent 头部的值

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
js 获取操作系统
function detectOS() {
var sUserAgent = navigator.userAgent;
var isWin = (navigator.platform == "Win32") || (navigator.platform == "Windows");
var isMac = (navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel");
if (isMac) return "Mac";
var isUnix = (navigator.platform == "X11") && !isWin && !isMac;
if (isUnix) return "Unix";
var isLinux = (String(navigator.platform).indexOf("Linux") > -1);
if (isLinux) return "Linux";
if (isWin) {
var isWin2K = sUserAgent.indexOf("Windows NT 5.0") > -1 || sUserAgent.indexOf("Windows 2000") > -1;
if (isWin2K) return "Win2000";
var isWinXP = sUserAgent.indexOf("Windows NT 5.1") > -1 || sUserAgent.indexOf("Windows XP") > -1;
if (isWinXP) return "WinXP";
var isWin2003 = sUserAgent.indexOf("Windows NT 5.2") > -1 || sUserAgent.indexOf("Windows 2003") > -1;
if (isWin2003) return "Win2003";
var isWinVista= sUserAgent.indexOf("Windows NT 6.0") > -1 || sUserAgent.indexOf("Windows Vista") > -1;
if (isWinVista) return "WinVista";
var isWin7 = sUserAgent.indexOf("Windows NT 6.1") > -1 || sUserAgent.indexOf("Windows 7") > -1;
if (isWin7) return "Win7";
}
return "other";
}

document.writeln("您的操作系统是:" + detectOS());

js判断浏览器

My Little World

关于CDN

发表于 2017-08-03

cdn 是一种从网络层面优化页面访问速度的手段
CDN服务商提供一项智能域名解析服务,这种智能DNS服务在浏览器发起域名查询时,会根据用户IP计算并返回离它最近的同网络CDN节点IP,引导浏览器与此节点建立连接以获取资源。
不同地区的用户访问同一个域名,依赖CDN服务商提供的智能域名解析服务得到不同CDN节点的IP地址
不同地区的用户会访问到离自己最近的相同网络线路上的CDN节点,当请求达到CDN节点后,节点会判断自己的内容缓存是否有效,如果有效,则立即响应缓存内容给用户,从而加快响应速度。如果CDN节点的缓存失效,它会根据服务配置去我们的内容源服务器获取最新的资源响应给用户,并将内容缓存下来以便响应给后续访问的用户。因此,一个地区内只要有一个用户先加载资源,在CDN中建立了缓存,该地区的其他后续用户都能因此而受益。
通过将静态资源缓存到离用户很近的相同网络运营商的CDN节点上,不但能提升用户的访问速度,还能节省服务器的带宽消耗,降低负载。
好的cdn服务到源服务器获取文件会通过专用的线路,这样就会比用户直接从源服务器获取资源快
cdn缓存技术原理

My Little World

常见页面布局小结

发表于 2017-08-03

圣杯布局

三栏布局,左右两栏宽度固定,中间一栏宽度随屏幕宽度自适应

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
<body>
<div id="header">#header</div>
<div id="container">
<div id="center" class="column">#center</div>
<div id="left" class="column">#left</div>
<div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>
</body>

<style type="text/css">
body {
min-width: 550px; /* 2x LC width + RC width */
}
#container {
padding-left: 200px; /* LC width */
padding-right: 150px; /* RC width */
}
#container .column {
position: relative;/* 让左右两栏进行相对位移,位移到container的内边距位置 */
min-height: 200px;/* 高度自适应*/
float: left;
}
#center {
width: 100%;
background-color: #e9e9e9;
}
#left {
width: 200px; /* LC width */
right: 200px; /*LC width *//*相对于 container 的右边线向左偏移 200px 将自己位移到container内边距 */
margin-left: -100%;/*浮动到与中间平行 */
background-color: red;
}
#right {
width: 150px; /* RC width */
margin-right: -150px; /* RC width */
background-color: blue;
}
#footer {
clear: both;/*清除 footer 的上下环境,以免遭跟上面三栏一起浮动*/
}
#header,
#footer {
background-color: #c9c9c9;
}
</style>

圣杯1
圣杯2

flex 实现 圣杯布局

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
<!doctype html>
<html>
<head>
</head>
<body>
<header>header</header>
<section>
<nav>nav</nav>
<article>article</article>
<aside>aside</aside>
</section>
<footer>footer</footer>
</body>
<style>
body{
display: flex;
flex-flow: column wrap;
justify-content:flex-start;
}
header,section,nav,aside,footer{
display: block;
}
header{
order: 1;
width: 100%;
min-height: 100px;
padding: 10px 20px;
background-color: antiquewhite;
}
section{
flex: 1;
order: 2;
min-width: 100%;
margin: 20px 0;
display: flex;
}
footer{
order: 3;
min-height: 60px;
min-width: 100%;
padding: 1%;
background-color: burlywood;
}
nav{
order:1;
width: 220px;
padding: 1%;
background-color: aquamarine;
}
article{
flex: 1;/* 可伸缩 */
order:2;
padding: 1%;
margin:0 2%;
background-color: blue;
word-break: break-all;
}
aside{
order:3;
width:220px;
padding: 1%;
background-color: aqua;
}

</style>
</html>

双飞翼布局

效果同圣杯布局,只是写法上DOM在center里面新增加了div,css不再使用relative定位,只是用float和外边距

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
<div id="header">header</div>
<div id="container">
<div id="center" class="column">
<div id="innerCenter">center</div>
</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>
</div>
<div id="footer">footer</div>

<style type="text/css">
body {
min-width: 351px; /* LC width + RC width +1*/
}
#container .column {
min-height: 200px;
float: left;
}
#innerCenter { /*代替圣杯container的padding处理*/
margin-left: 200px;
margin-right: 150px;
}
#center {
width: 100%;
background-color: #e9e9e9;
}
#left {
width: 200px;
margin-left: -100%;
background-color: red;
}
#right {
width: 150px;
margin-left: -150px;
background-color: blue;
}
#footer {
clear: both;
}
#header, #footer {
background-color: #c9c9c9;
}

</style>

圣杯3
圣杯4

7种垂直居中

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
1.tabel自带
<body>
<table class="parent">
<tr>
<td class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</td>
</tr>
</table>
.parent{
border: 1px solid red;
height: 600px;
}

.child{
border: 1px solid green;
}
2.在水平居中部分的前后添加inline-block元素,高度100%,三个子元素,vertical-align:middle
<body>
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>
</div>
</body>
.parent{
border: 3px solid red;
height: 600px;
text-align: center;
}

.child{
border: 3px solid black;
display: inline-block;
width: 300px;
vertical-align: middle;
}

.parent:before{
content:'';
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
.parent:after{
content:'';
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
如果用标签元素,则前后元素类设置为.parent .after{},.parent .before{}

3.父元素position设置为相对定位relative,子元素位置相对父元素,设置为absolute,通过设置top和left 50%决定距离父元素上边和左边的距离,
再通过margin-top,margin-left为子元素高和宽的一半将子元素和父元素中心重合
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>
</div>
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
width: 300px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin:-50px 0 0 -150px;
}


4.父元素relative,子元素absolute,top和left均为 50%,transform:translate(-50%,-50%)
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>
</div>
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}

5.父元素relative,子元素absolute,子元素,上下左右都是0,margin:auto
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>
</div>
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
width: 300px;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
6.felx布局,父元素display:flex,justify-content:center;align-items:center
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>

</div>
.parent{
height: 600px;
border: 3px solid red;

display: flex;
justify-content: center;
align-items: center;
}
.child{
border: 3px solid green;
width: 300px;
}

清除浮动

1.在父元素上添加样式overflow:hidden/auto
2.给父元素设置高度或者添加float属性(使之成为浮动元素)
3.在子元素结尾添加空div标签或者br标签,或者给父元素添加:after伪类,设置属性clear:both

1
2
3
4
5
6
.clearfix::after{
content: ''; display: block; clear:both;
}
.clearfix{
zoom: 1; /* IE 兼容 */
}

多列布局

column-width —— 列宽,不设置时由其他属性决定
column-count —— 列数,不设置时由其他属性决定
columns —— column-width column-count

column-gap —— 列间距
column-rule —— 间距线,位于列间距中间,介于background和content之间,宽度大于列间距时消失
宽度 样式 颜色

column-fill —— 各列宽度
auto 随内容自动调整 |balance 以内容最多一列统一高度
column-span —— 子元素是否合并列
none/all

静态布局:尺寸不变,布局不变
自适应布局:为不同尺寸屏幕设计不同静态布局
流式布局:通过更改模块大小,保证整体布局
响应式布局:针对不同尺寸屏幕,设计不同流式布局
弹性布局:可以设置模块大小放缩保证布局,也可以固定模块大小,更改布局
各种布局概念区分
react涉及布局
相关资料
CSS布局 ——从display,position, float属性谈起

My Little World

使用QRcode.js生成二维码

发表于 2017-08-01

在页面中引入QRcode.js文件,再js部分进行定义
var qrcode = new QRCode(document.getElementById(“qrcode0”), { //qrcode0放置二维码容器的id
width : 150,//二维码大小
height : 150
});
qrcode.makeCode(url);//url,生成二维码的依据

My Little World

Angularjs 利用图片更换emoji表情

发表于 2017-08-01

在某些浏览器中,emoji表情不能正常显示,只能显示原始状态如下
erroremoji
为使表情正常显示,这里利用转码方法将emoji符号用图片替换

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
//对数据$scope.data.remark做表情替换处理,$scope.data.remark是包含emoji符号的字符串
var str=$scope.data.remark;
str=str.replace(rep,function(code) { //code即匹配到的emoji符号,对该符号通过_escapeToUtf32(code)转码,得到对应图片名,然后图片替换
return '<img class="emoji" style="vertical-align:middle" src="assets/img/emoji/'+_escapeToUtf32(code) + '.png">';
})
$scope.data.remark = str;

//用emoji符号的unicode码匹配emoji符号,例如270c匹配 victory hand符号
var rep =/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]|\u270c|\u261d/g;

//emoji 表情转码
function _escapeToUtf32(str) {
var escaped = [],
unicodeCodes = _convertStringToUnicodeCodePoints(str),
i = 0,
l = unicodeCodes.length,
hex;

for (; i < l; i++) {
hex = unicodeCodes[i].toString(16);
escaped.push('0000'.substr(hex.length) + hex);
}
return escaped.join('-');
}

function _convertStringToUnicodeCodePoints(str) {
var surrogate1st = 0,
unicodeCodes = [],
i = 0,
l = str.length;

for (; i < l; i++) {
var utf16Code = str.charCodeAt(i);
if (surrogate1st != 0) {
if (utf16Code >= 0xDC00 && utf16Code <= 0xDFFF) {
var surrogate2nd = utf16Code,
unicodeCode = (surrogate1st - 0xD800) * (1 << 10) + (1 << 16) + (surrogate2nd - 0xDC00);
unicodeCodes.push(unicodeCode);
}
surrogate1st = 0;
} else if (utf16Code >= 0xD800 && utf16Code <= 0xDBFF) {
surrogate1st = utf16Code;
} else {
unicodeCodes.push(utf16Code);
}
}
return unicodeCodes;
}

//Angularjs对字符串里面的html标签不会按照HTML去解析,会当做字符串显示,
//所以这里需要对字符串进行过滤,使字符串里面的标签能按照html解析

//新建过滤器trust2Html,这里使用$sce方法
app.filter('trust2Html', ['$sce',function($sce) {
return function(val) {
return $sce.trustAsHtml(val);
};
}])

//在页面里面显示经过图片替换的含emoji符号的字符串
<label ng-bind-html="data.remark|trust2Html"></label>

替换后结果
exactemoji
Emoji Unicode Tables

1…171819…27
YooHannah

YooHannah

262 日志
1 分类
23 标签
RSS
© 2025 YooHannah
由 Hexo 强力驱动
主题 - NexT.Pisces