My Block


  • Startseite

  • Archiv

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

Veröffentlicht am 2018-04-07

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 个报文传输周期。

未完待续…

SASS入门

Veröffentlicht am 2018-04-06

1. Sass和SCSS的区别

SCSS是Sass推出的一个全新的语法,更加贴近css,所以一般都是用SCSS。
请注意 Sass 从来没有大写过,无论你指的是语法或者这个语言。同时, SCSS 一直是大写的!

2. 安装(mac)

我安装的是node-sass
在命令行里进入~目录,输入:

  • npm config set registry https://registry.npm.taobao.org/
  • echo 'export SASS_BINARY_SITE="https://npm.taobao.org/mirrors/node-sass"'>> ~/.bashrc(若是用的zshrc则改为~/.zshrc)
  • source ~/.bashrc(同上)
  • sudo cnpm i -g node-sass(需要先安装cnpm)
  • mkdir ~/Desktop/scss-demo
  • cd ~/Desktop/scss-demo
  • mkdir scss css
  • touch scss/style.scss
  • node-sass style.scss style.css -w style.scss(开始监听style.scss)

3. 使用

终端进入含css和scss文件的目录,执行
node-sass style.scss style.css -w style.scss(开始监听main.scss)
直接编辑main.scss,node-scss就会自动给main.css实时更新。
在 scss 文件里添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@function px( $px ){
@return $px/$designWidth*10 + rem; //js里获取innerWidth的时候除以了10,这里就要乘以10
}

$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;
}

可看到css变化,SCSS将px变成了rem放入了css中。
若不使用SCSS的话,我们每次将px转化为rem都要自己去算,使用SCSS可以帮我们算

4. 语法

1
2
3
4
5
.topNavBar{
nav{
background: red;
}
}

jQuery入门

Veröffentlicht am 2018-04-03

1. 引用

jquery下载页面,下载为js文件,在html中引用即可

2. 基础

jQuery的原型为jQuery.prototype

1
2
var div = document.getElementById('x')  //dom对象
var $div = $('#x') //jquery对象

div和$div的联系:$div[0] === div
$div === $(div)
区别:div是id为x的元素
&div是对象

3. API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$nodes.addClass('red');
$nodes.addClass(function(index,currentClass){
return 'sd-'+index; //这样nodes里每一项会依次添加sd-0 sd-1 sd-2 sd-3 ......的class
});
var classes = ['red','blue','black'];
$nodes.addClass(function(index,currentClass){
return classes[index]; //前三项就会添加red、blue、black的class
});

$nodes.removeClass('red');

$(x).on('click', function(){ //建一个button并给它id为x
$nodes.toggleClass('red'); //每点一次button,nodes就增加或删除'red'class
})

4. jQuery对象和DOM对象的转换

jquery转dom

1
var div = $div[0]  //这样jquery就转换成了DOM对象,或div = $div.get(0)

dom转jquery

1
var $div = $(div);  //这样dom对象div,就转换成了jquery对象

JS函数

Veröffentlicht am 2018-03-30

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

JS标准库和数组

Veröffentlicht am 2018-03-27

1. String

1
2
String(s);    //'s',不加new 就直接输出字符串
new String(s) //String{'s'},加new就输出一个对象

2. Number

1
2
3
4
5
Number(sss)    //报错,Error: sss is not defined
Number('sss') //NaN,NaN也是一个数字
Number(1) //1
Number('1') //1
new Number(123) //Number{123},加new就变成对象

3. Boolean

1
2
3
4
5
6
7
8
9
6个falsy值
Boolean(false) //false
Boolean(undefined) //false
Boolean(null) //false
Boolean(NaN) //false
Boolean('') //false
Boolean(0) //false

其他的全是true

4. 基本类型和复杂类型的区别

数据类型.png

除了对象(包括数组、函数)是复杂类型,其他数据类型都是基础类型

5. Object

1
2
3
Object(1);    //Number{1},前加new 和不加new是一样的
Object('s') //String{'s'}
Object() //{}

6. Array

1. 用法
第一种用法:

1
2
3
4
5
6
7
8
9
10
let a = ['a','b'];    //['a','b']
let b = new Array('a','b'); //['a','b']
//上面两种写法效果是一样的

let a = Array(3) //创建了一个长度为3的空数组
a.length //3
a[0] //undefined
0 in a //false

let a = new Array(3) //和上面一样

第二种用法:

1
2
let a = Array(3,3);    //[3,3],length为2
//这和第一种一样的写法,功能却不同,不一致性,JS的垃圾之处

2. 什么是数组?数组.png数组就是对象,是拥有特殊原型链的对象,有Array.prototype的就是数组,没有的就不是数组
3. 伪数组

1
2
3
4
5
let obj = {
0:'1',
1:'2',
length:2;
} //看起来像数组,但没有Array.prototype的,就是伪数组

arguments是伪数组

4. 数组的API

  1. forEach

    1
    2
    3
    4
    5
    6
    7
    8
    var a = ['a','b','c'];
    a.forEach(function(x,y){
    console.log(y); //y是key
    console.log(x); //x是value
    })
    //0 a
    //1 b
    //2 c
  2. sort,这个API会改变原值!只有这一个API会改变原值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var l = [2,3,1];

    l.sort(); //[1,2,3],从小到大排序
    l.sort(function(a,b){return a-b}) //[1,2,3],从小到大排序
    l.sort(function(a,b){return b-a}) //[3,2,1],从大到小排序

    //按成绩高到低排名
    var students = ['小明','小红','小花'];
    var scores = { 小明: 59, 小红: 99, 小花: 80 };
    students.sort(function(x,y){return scores[y] - scores[x]}) // ["小红", "小花", "小明"]
  1. join

    1
    2
    3
    4
    var a = [1,2,3];

    a.join('方方'); //'1方方2方方3'
    a.join(); //'1,2,3',默认是逗号
  2. concat

    1
    2
    3
    4
    5
    var a = [1,2,3];
    var b = [3,4,5];
    var c = a.concat(b); //'1,2,3,3,4,5'

    var d = a.concat([]); //'1,2,3',d和a的值一样,但d和a是两个数组,所以concat可以用来深拷贝数组
  3. map(映射)

    1
    2
    3
    4
    5
    var a = [1,2,3];
    a.map(function(value,key){
    return value * 2;
    }) //[2,4,6];
    a.map(value => value * 2); //[2,4,6]
  4. filter(过滤器)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var a = [1,2,3,4,5];
    a.filter(function(value,key){
    return value >= 3;
    }) //[3,4,5]
    a.filter(function(value,key){
    return value % 2 === 0 ;
    }) //返回偶数,[2,4]

    a.filter(function(value,key){
    return value % 2 === 0 ;
    }).map(function(value){
    return value * value;
    }) //[4,16];
  5. reduce

    1
    2
    3
    4
    5
    6
    var a = [1,2,3];
    a.reduce(function(sum,n){
    return sum + n ;
    },0) //6,求和,0是初始值,第一次传给sum的就是0,sum是每次传进来的值,之后传给sum的是return里的值,n是遍历的数

    a.reduce((sum,n) => sum + n ,0) //6,箭头函数的写法

map可以用reduce表示:

1
2
3
4
a.reduce(function(arr,n){
arr.push(n*2);
return arr;
},[]) //[2,4,6]

filter也可以用reduce表示:

1
2
3
4
5
6
a.reduce(function(arr,n){
if(n % 2 === 0){
arr.push(n)
}
return arr;
},[])

只要掌握reduce就行了

  1. splice

    1
    2
    3
    array.splice(start)  //start第一个位置是0
    array.splice(start, deleteCount) //若不写删除的个数,则会把包括start位置的数据和后面的所有数据都删除,并返回它们
    array.splice(开始的位置, 删除的个数, 插入的数据1, 插入的数据2, 3...)

    splice会改变数组本身的数据!!

  2. slice
    slice不会改变数组本身数据!!
    1
    2
    3
    array.slice()  //或array.slice(0),返回完整数组
    array.slice(4) //返回位置4到最后的数据
    array.slice(4,8) //返回位置4到8以前的数据(不包括8)

JavaScript Dom入门

Veröffentlicht am 2018-03-25

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内存简介

Veröffentlicht am 2018-03-23

内存

下面介绍浏览器里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原型

Veröffentlicht am 2018-03-23

1. 公用属性(就是原型)

每个对象都有toString()、valueof()等方法,但他们并其实没有单独的方法,而是JS在每个对象里都存了一个隐藏属性:__proto__,这个属性指向了存放公用属性的地址(Object.prototype),当用toString()时,就是调用的公用属性

1
o1.toString === o2.toString   //true

同时Number对象还有它特有公用属性,所以它比Object要多一个公用属性库,并且会先访问Number的原型(公用属性库),这一条链就叫原型链,像下图原型链
同理,String、boolean也有自己的原型,但若平常没被引用的话会被当作垃圾回收,所以平常对象的原型是Object.prototype在引用,可得⬇️

1
Object.prototype === o1.__proto__     //true,是把prototype的地址赋给__proto__属性了

所以它们最终指向的Object的公有属性,就是Object.prototype(原型),Number、String的特有公有属性也是它们的prototype属性在引用,同理有

1
2
3
4
5
Number.prototype.__proto__ === Object.prototype //true,是把Object.prototype的地址赋给Number的__proto__属性了

var n1 = new Number(1);
n1.__proto__ === Number.prototype //true,是把Number.prototype的地址赋给n1的__proto__属性了
n1.__proto__.__proto__ === Object.prototype //true

2. __proto__和prototype的区别

原型.png
打开浏览器就相当于生成了一个window,里面存了Number、String、Boolean、Object这几个对象,对象里的prototype属性里存了它们原型的地址,在你写:

1
var n = new Number(1);     //就会把Number.prototype里的地址放入n的__proto__属性里

这样用n就能调用Number的原型了

总结:

1
2
var 对象 = new 函数();		//函数可以是Number/String/Boolean/Object
对象.__proto__ === 函数.prototype //true

__proto__是对象的属性,prototype是函数的属性

面试题:'1'.__proto__是什么?
答:’1’会临时转化为String对象

1
'1'.__proto__ === String.prototype    //true

所有对象都有__proto__属性

1
2
3
4
Object.prototype.__proto__ === null;

1.toString() //语法错误,不加引号JS会把.当作小数点
1..toString() //'1',第一个.当作小数点,第二个为点操作符

JS里的对象

Veröffentlicht am 2018-03-23

1. 全局变量:一种是 ECMAScript 规定的(global)

  • global.parseInt
  • global.parseFloat
  • global.Number
  • global.String
  • global.Boolean
  • global.Object

2. 全局变量:另一种是浏览器自己的全局变量(window)

  • window.alert
  • window.prompt
  • window.comfirm
  • window.console.log

3. 上面两种全局变量都是stack(栈内存)里的重要变量

4. Number

JavaScript的作者Brendan Eich被公司要求JS要像Java,所以他模仿java写了
var n = new Number(1);这样n是一个对象,有自己的属性,但太麻烦,于是他又写了一种
var n =1;n是基本类型
但他用了一个方法,使这样声明的也可以用Number对象的属性

1
2
3
4
5
6
7
8
var n=1;
n.toString(); //n用toString时,本质是下面这两行,实质n是没有属性的

var temp = new Number(n); //声明了一个临时变量temp,并将n作为Number对象赋给它
temp.toString(); //用temp来执行toString,执行完后temp就被抹杀了

n.xxx = 2; //可以的
n.xxx //但undefined,因为xxx是存在temp的,上一句的temp执行完后就被抹杀,所以xxx也被抹杀了

5. String

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
var s = 'asd';      //     原理和Number一样

var s2 = new String(s);
s2[0] // 'a',因为String()对象有hash的属性

s[0] // 'a',s也能调用这个属性,但s实质没有属性,这个原理和上面Number一样

s.charCodeAt(0) // 97,a的unicode码
s.charCodeAt(0).toString(16) //'61',a的unicode码的十六进制

' asd '.trim(); // 'asd',去掉字符串两边的空格

var s1 = 'Hello';
s.concat(s1); // asdHello,连接两个字符串

s1.slice(0,1); // 'H',slice是切片,切下0到1之前的内容
s1.slice(1); // 'ello',返回1和后面的所有内容

s1.replace('e','o') // 'Hollo',替换

s1.length // 5

s1.indexOf('o') // 4,定位,若没找到会返回-1
if(s1.indexOf('h') !== -1) { // 可用来执行某些操作
// do stuff with the string
}

s1.toLowerCase(); // 全变成小写
s1.toUpperCase(); // 全变成大写

6. Boolean

6个falsy值,0、NaN、null、undefined、false、””,除了这6个,其他值都是true
所有对象都是true

1
2
3
4
5
var b = false;
var b1 = new Boolean(false);
if(b){console.log('true')};
if(b1){console.log('true')}; // true
//只会输出b1,因为所有对象都是true!!

7. Object

1
2
3
4
var o1 = {};      //常用方法
var o2 = new Object();

o1 === o2 //false,因为比较的是地址,而地址不同

JavaScript入门2(阮一峰)

Veröffentlicht am 2018-03-21

1. 数组

数组本身是对象,是一种特殊的对象

1.length

数组不能用点结构读取

1
2
3
4
5
6
7
var a = [1,2];
a[0] === 1 //true
a.0 // SyntaxError: Unexpected number
a.length === 2 // true,数组默认有length值

a[9] = 10;
a.length === 10 // true,JavaScript还是会认为长度是10,会把中间的值都当作empty

JavaScript用32位整数保存数组的长度,所以数组最多只能存2^32^-1个数,数组length的值是可以设置的,a.length = 0可以用来清空数组。

1
2
3
var a = [1,2,3];
a.length = 0;
a // []

length的长度是可以设置的,若length大于原来的值,则多出来的叫空位,读取空位会返回undefined,用in运算符会返回false。

1
2
3
4
5
6
var a = [1,2,3];
a.length = 10
a[5] // undefined

2 in a // true,in检测的是key,而不是value和length
3 in a // false,in检测的是key,而不是值

将数组的键分别设为字符串和小数,结果都不影响length的值,所以length只和数字键有关。

1
2
3
4
5
6
var a = [];

a['p'] = 'abc';
a.length === 0 //true
a[2.1] = 'abc';
a.length === 0 //true

如果数组的键名是添加超出范围的数值,该键名会自动转为字符串。

1
2
3
4
5
6
a[-1] = 'a';
a[Math.pow(2, 32)] = 'b';

a.length === 0 //true
a[-1] === "a" //true
a[4294967296] === "b" //true

2. in

in检测的是键名,而不是值

1
2
3
4
5
6
7
8
var arr = [ 'a', 'b', 'c' ];
2 in arr // true
'2' in arr // true
4 in arr // false ,不存在就是false

arr[100] = 'a';
100 in arr // true
99 in arr // false,空位也会被认为是false

3. 遍历数组

1
2
3
4
var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) { //function里的参数就是每一项的值
console.log(color);
}); //red green blue

用length遍历数组时,会把空位输出为undefined,但使用数组的forEach方法、for…in结构、以及Object.keys方法进行遍历,空位都会被跳过,只有当数组的值是undefined时,它们才会输出undefined,所以用length遍历数组时要小心空位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a = [ ,2,3];

for(var i=0;i<a.length;i++){
console.log(a[i]); //undefined 2 3
}

a.forEach(function (x, i) { //x是value,i是key
console.log(i + ':' + x); // 1:2 2:3
});

a = [undefined,2,3];

a.forEach(function (x, i) {
console.log(i + ':' + x); // 0:undefined 1:2 2:3
});

4. delete

1
2
3
4
5
var a = [1, 2, 3];
delete a[1];

a[1] // undefined
a.length // 3

delete不会影响length,所以用length遍历数组的时候要小心

5.类似数组的对象

可以把类似数组对象转为真正的数组(目前看不懂),详情见阮一峰-数组-类似数组的对象

6.

1
typeof [1,2,3];      //      'object'  注意返回字符串
12345

Pengyize

50 Artikel
© 2018 Pengyize
Erstellt mit Hexo
|
Theme — NexT.Muse v5.1.4