Marco Nie - 2020年12月 https://blog.niekun.net/2020/12/ zh-CN you are the company you keep... Tue, 29 Dec 2020 13:58:00 +0800 Tue, 29 Dec 2020 13:58:00 +0800 CentOS 6 可用源 https://blog.niekun.net/archives/2035.html https://blog.niekun.net/archives/2035.html Tue, 29 Dec 2020 13:58:00 +0800 admin 最近在调试软件的时候需要用到 CentOS 6 系统,yum 命令无法更新包返回都是 404,查了下发现大多数主流的源地址都取消对 CentOS 6 的支持,找了半天才找到一个可用的地址,记录下来备用。

CentOS 6 的包列表在:/etc/yum.repos.d/CentOS-Base.repo 文件中定义。

参考链接:https://blog.csdn.net/h952520296/article/details/110541018

# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the 
# remarked out baseurl= line instead.
#
#
 
[base]
name=CentOS-6.10 - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos-vault/6.10/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos-vault/RPM-GPG-KEY-CentOS-6
 
#released updates 
[updates]
name=CentOS-6.10 - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos-vault/6.10/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos-vault/RPM-GPG-KEY-CentOS-6
 
#additional packages that may be useful
[extras]
name=CentOS-6.10 - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos-vault/6.10/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos-vault/RPM-GPG-KEY-CentOS-6
 
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-6.10 - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos-vault/6.10/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos-vault/RPM-GPG-KEY-CentOS-6
 
#contrib - packages by Centos Users
[contrib]
name=CentOS-6.10 - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos-vault/6.10/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos-vault/RPM-GPG-KEY-CentOS-6

将以上内容替换到 CentOS-Base.repo 文件内,然后执行:

yum clean all && yum makecache

更新缓存即可。

]]>
0 https://blog.niekun.net/archives/2035.html#comments https://blog.niekun.net/feed/archives/2035.html
Windows 补丁下载 https://blog.niekun.net/archives/2021.html https://blog.niekun.net/archives/2021.html Tue, 22 Dec 2020 10:54:50 +0800 admin 如果需要单独下载某个 Windows 补丁,可以在其官网下载:https://www.catalog.update.microsoft.com/

搜索补丁编号即可。

]]>
0 https://blog.niekun.net/archives/2021.html#comments https://blog.niekun.net/feed/archives/2021.html
JavaScript 入门教程之八 -- ES6 https://blog.niekun.net/archives/2011.html https://blog.niekun.net/archives/2011.html Mon, 21 Dec 2020 22:13:00 +0800 admin ECMAScript (ES) 就是标准 JavaScript 的脚本语言规范。

它的第 6 个版本,开始被叫做 ECMAScript 6 (ES6) 然后改名为 ECMAScript 2015,为编写更加复杂的程序添加了很重要的新语法。包括了:classes and modules, iterators and for/of loops, generators, arrow functions, binary data, typed arrays, collections (maps, sets and weak maps), promises, number and math enhancements, reflection, and proxies。

ES6 是 ES5 的超集,ES6 现在非常流行,原因就是它引入了新的约定和 OOP 的概念,如:classes。下面我们介绍一些 ES6 中最核心的一些特性。

需要注意的是只有浏览器支持 ES6 的情况下才能够执行 ES6 指令,否则会报错。

var & let

ES6 中有三种方式定义变量:

var a = 10;
const b = "test";
let c = true;

使用那种类型来申明变量取决于变量需要使用的 scope 范围。scope 是所有编程语言的基本概念,它定义了变量的可见范围。

var 关键词定义的变量是全局有效的,或者在整个 function 内有效,而与具体在那个代码块内定义它无关。

let 可以定义一个变量在某个特定 scope 内有效,如一个代码块或一个表达式内。

例如:

if (true) {
    let age = 5;
}
alert(age);

以上代码会报错 ES6.html:21 Uncaught ReferenceError: age is not defined,可以打开浏览器调试窗口查看:
1.jpg

这种情况下,name 变量只能在 if 表达式内被使用,因为它使用 let 申明。

测试 varlet 的区别,我们做下面的示例:

<script>
    function varTest() {
        var x = 1;
        if (true) {
            var x = 2;
            console.log(x); // 2
        }
        console.log(x); // 2
    }

    function letTest() {
        let x = 3;
        if (true) {
            let x = 4;
            console.log(x); // 4
        }
        console.log(x); // 3
    }
    varTest();
    letTest();
</script>

输出结果为:

2
2
4
3

varTest() 中定义的两个 x 实际上是同一个变量。letTest() 中 if 表达式中的 x 和外围的 x 不是一个变量。

let 最常使用的地方就是 for 循环中:

for (let index = 0; index < 3; index++) {
}

index 只在 for 循环内可以被使用。

const 变量和 let 变量一样都在 scope 内有效。不同点是 const 变量的值是不可变的,不能够被重新分配数据。

以下指令会报错:

const a = 1;
a = 2;

Template Literals

Template literals 文字模板是将变量输出到字符串的一种方式,ES6 之前需要打断字符串:

var name = "marco";
var msg = "hello" + name + "!";
console.log(msg);

ES6 引入了一种新的方式来处理:

var name = "marco";
var msg = `hello ${name}!`;
console.log(msg);

template literals 文字模板使用 ` 符号来代替引号 "'

${expression} 是一个占位符,可以包含任何表达式,将评估的结果返回后嵌入 template literals 文字模板中。

例如:

var a = 1;
var b = 2;
var msg = `sum is ${a + b}`;
console.log(msg);

Loops and Functions in ES6

在 JavaScript 中常用 for 循环来 iterate list 中的元素:

let array = [1, 2, 3];
for (let index = 0; index < array.length; index++) {
    const element = array[index];
}

for...in 循环可以用来 iterating 一个含有一定数量的 properties 的 object:

let obj = {
    a: 1,
    b: 2,
    c: 3
};
for (let key in obj) {
    console.log(obj[key]);
}

log 输出为:

1
2
3

注意 for...in loop 不能 iterate 一个 array 数组。虽然数组也是一个 object,但他的 index 索引是基于 number 数字的。基于 JavaScript engine,for...in 循环 iterate 某个 list 的元素的顺序是随机的,而且 iterating 索引是一个 string 字符串的,而不是 number 数字,所以当你测试对这个索引进行某些 math 数学加法运算,会发现执行的是对字符串的串联而不是数字运算。

ES6 中引入了一个 for...of loop 能够创建一个对 iterable object 进行 iterating 的循环,数组就是一种 iterable object,而含有 name: value 结构属性的 object 不属于。后面我们会介绍实际上是通过 Symbol.iterator 来索引数组类型的 object 的。

例如:

let array = [1, 2, 3];
for (const iterator of array) {
    console.log(iterator);
}

在每个循环中,iterator 变量都会赋值为 object 内的坐标元素。

for...of loop 适用于其他 iterable objects 例如 string 字符串:

for (const iterator of "object") {
    console.log(iterator);
}

输出结果为:

o
b
j
e
c
t

for...of 同样适用于 ES6 新引入的一些集合:Map, Set, WeakMap, and WeakSet,后面我们会做介绍。

Functions in ECMAScript 6

ES6 之前定一个 function 的方法如下:

function add(a, b) {
    let sum = a + b;
    console.log(sum);
}

ES6 介绍了一种新的语法,效果和上面的示例完全一样:

const add = (a, b) => {
    let sum = a + b;
    console.log(sum);
}

这种写法叫做 arrow function,对于只有一个参数的简单 function 非常好用,可以省略关键词 functionreturn 甚至是大括号{}和小括号()

const greet = x => "welcome" + x;

以上代码定义了一个 greet function,有一个参数和一个 string 返回值。

如果 function 没有参数,需要加一个小括号()

const x = () => alert("hi");

这种语法对于 inline function 很有用。

假如有一个数组,需要对其每一个元素都执行某个 function,使用 array 的 forEach method 来为每个元素调用 function,传统写法如下:

var arr = [1, 2 ,3 ];
arr.forEach(function(el) {
    console.log(el * 2);
});

在 ES6 中,以上功能可以重写为:

arr.forEach(el => {
    console.log(el * 2);
});

代码是不是简化很多呢?

Default Parameters in ES6

之前的 function 中定义参数的默认值可以这样:

function test(a, b = 2, c = 3) {
    console.log(a + b + c);
}

使用 arrow function 可以这样定义:

const test = (a, b = 2, c = 3) => {
    console.log(a + b + c);
}

ES6 objects

JavaScript 中 object 内可以定义多个 variable 变量,叫做 properties。properties 定义了 function 的叫做 method,例如:

var person = {
    name: "marco", age: 20,
    like: "basketball", height: 62,
    test: function() {
        alert("method");
    }
};

test 就是一个 method,ES6 引入了一种简化的语法和 properties 名称来使定义更加方便和易懂。

新的语法定义 method 不需要冒号:functon 关键词:

let person = {
    test() {
        alert("hi");
    }
};
person.test();

当使用已知变量定义 properties 且定义名称和变量名称一样时,可以简写语法如下:

let height = 160;
let weight = 60;

let man = {
    height,
    weight
};

以上定义中 man object 就定义了两个 properties:heightweight,且赋值为外部调用变量的值。

当在 object 中定义了相同名称的 properties,最后一个定义的 property 将会覆盖前面的:

let x = {a: 2, a: 3, a: 4};
console.log(x.a);

输出结果为:4

在 ES5 中如果使用了 restrict 限制模式,定义重复名称的 properties 将会报语法错误,ES6 中取消了这个限制

Computed Property Names 预定义属性名

ES6 中,可以使用 computed property name 预定义属性名。通过方括号[] 我们可以在 properties name 中使用一个表达式,可以进行包括串联字符串或数学运算等指令。当需要创建一个基于实际用户数据(id, email等)的特定 object 时非常有用。

示例 1:

let name = "marco";
let id = "123";
let tel = "1300000";

let user = {
    [name]: "tom",
    [`user_${id}`]: `${tel}`
};
console.log(user.user_123);

//output:
//1300000

示例 2:

let i = 0;

let x = {
    ['foo' + ++i]: i,
    ['foo' + ++i]: i
}
console.log(x.foo1);
console.log(x.foo2);

//output:
//1
//2

示例 3:

var par = 'size';

var config = {
    [par]: 1,
    ['mobile' + par.charAt(0).toUpperCase() + par.slice(1)]: 2
}
console.log(config.mobileSize);

//output:
//2

当需要创建基于变量的自定义 object 时,这种方法很有效。

也可以在调用 object 属性是使用预定义属性的特性,下面我们定义一个 object 然后通过方括号通过另一个变量间接调用其属性:

   const obj1 = {
       name: 'marco',
       age: 32
   }


   const choice = 'age';
   console.log(obj1[choice]);

ES6 object 新增了一个新的 method:assign() 可以用来将多个 source 源结合起来创建一个新 object。assign() 也可以用来创建一个已知 object 的副本。

请看下面示例:

let user1 = {
    name: 'marco',
    age: 20,
    sex: 'male'
}

let user2 = {
    name: 'jim',
    age: 18,
    tel: '12345'
}

let newUser = Object.assign({}, user1, user2);
console.log(newUser.name);
console.log(newUser.tel);

//output:
//jim
//12345

Object.assign() 的第一个参数表示需要添加新 properties 的目标 object,第一个参数后的所有参数都会被作为 source 源,源参数的数量没有限制,可以任意多个。

但是源参数的顺序很重要,因为前面的源 properties 将会被后面的有着同样属性 name 的源 properties 覆盖。例如上面示例中,user1 的 name 和 age properties 会被 user2 的同样名称的 properties 覆盖。

以上示例,我们使用{} 作为目标 object,使用两个 object 作为源。

下面我们介绍如何使用 assign() 创建一个 object 复制,新 object 不和原 object 产生关联。

在下面的示例中,我们使用简单的 assignment = 来创建新 object,但是这种方式会在 object 和原 object 间产生 reference 关联。对新 object 的修改会影响到原 object:

let person = {
    name: 'marco',
    age: 20
};

let newPerson = person;
newPerson.name = "tom";
console.log(newPerson.name);
console.log(person.name);

//output:
//tom
//tom

可以看到 修改 newPersonname 属性也会同时影响到 personname 属性。

为了避免这种情况,可以使用 Object.assign() 来新建 object:

let person = {
    name: 'marco',
    age: 20
};

let newPerson = Object.assign({}, person);
newPerson.name = 'tom';
console.log(newPerson.name);
console.log(person.name);

//output:
//tom
//marco

我们也可以在 assign() 中直接给 properties 赋值:

let person = {
    name: 'marco',
    age: 20
};

let newPerson = Object.assign({}, person, {name: 'john'});
console.log(newPerson.name);
console.log(person.name);

//output:
//john
//marco

以上就是 ES6 中 object 的新语法介绍。

Array Destructuring in ES6 数组拆解

destructuring assignment 拆解赋值语法能够实现将一个 array 的 values 拆分,或者将一个 object 的 properties 拆解为独立的个体。

Destructuring array

ES6中引入的快捷语法来拆解 array 数组,下面的示例介绍如何将数组元素拆解为独立个体:

let arr = [1, 2, 3];
let [a, b, c] = arr;

console.log(a);
console.log(b);
console.log(c);

也可以拆分一个 function 返回的数组:

let a = () => {
    return [1, 2, 3]
};

let [a, , c] = a();

注意上面示例中,第二个参数留空了,表示 a 赋值数组第一个元素,c 赋值数组第三个元素。

以上 function 使用了 ES6 的简写语法,参考上一节的介绍。

destructuring 拆分语法同样能够简化赋值和交换数据:

let a, b, c = 3, d = 4;

[a, b = 2] = [1]; //a=1, b=2
[c, d] = [d, c]; //c=d, d=c

console.log(a);
console.log(b);
console.log(c);
console.log(d);

//output:
//1
//2
//4
//3

以上的语法再一些使用场景中能够很大简化代码量,使程序更加简洁。

Destructuring object

类似于拆解数组,也可以拆解 object 的 properties 为单独个体:

let obj = {h: 1, s: 2};
let {h, s} = obj;

console.log(h);
console.log(s);

注意定义的新变量名需要和 object property 名称一致,否则新变量的值为:undefined

也可以不声明新变量而直接赋值,但有语法要求。需要加小括号()

let obj = {h: 1, s: 2};
let h, s;
({h, s} = obj);

拆解时可以给 object 的 properties 定义新的名称,使用: name: newname 形式:

let obj = {h: 1, s: 2};
let {h: a, s: b} = obj;

console.log(a);
console.log(b);

这时候如果执行:console.log(h); 会报错。

最后,我们可以给新变量设置初值,如果 object 中没有定义这个属性,新变量就会使用初值:

let user = {name: "marco", id: 123};
let {name = "tom", age = 20} = user;
console.log(name);
console.log(age);

//output:
//marco
//20

以上示例中,user object 含有 name 属性,则赋值为对应数据,没有 age 属性则会使用默认值作为新变量的数据。

ES6 Rest Parameters

在 ES6 之前,如果调用 function 时传入的参数数量是变化的,我们可以使用 arguments array object 来访问这些传入数据。下面示例中,我们创建一个 function 来检查传入参数值是否都在一个给定数组内:

function containsAll(arr) {
    for (let index = 1; index < arguments.length; index++) {
        let num = arguments[index];
        if (arr.indexOf(num) == -1) {
            return false;
        }
    }
    return true;
}

let x = [2, 4, 6, 8];
console.log(containsAll(x, 2, 4));
console.log(containsAll(x, 3, 4, 6));

//output:
//true
//false

可以看到 function 预定义的参数只有一个,我们在调用时传入了大于 1 个的参数,这时候在 function 内就需要使用 arguments 数组来索引传入数据。arguments[0] 代表第一个参数,也就是预定义的 arr 参数,其他数据在 arguments 数组中往后依次排列。

我们使用了 arrayindexOf method 来获取某个数据在数组中的 position 位置,如果数组中不存在这个数据,则返回 -1,这里我们使用了 === identity operator 来确保数据类型和数值都要匹配上。

我们可以给 function 传入任意数量的数据,然后使用 arguments 数组来访问它们。

ES6 中通过使用 rest parameter 剩余参数创建了一种更加简洁的语法,来访问变化的传入参数数据:

function containsAll(arr, ...nums) {
    for (const iterator of nums) {
        if (arr.indexOf(iterator) == -1) {
            return false;
        }
    }
    return true;
}

...nums 叫做 rest parameter 剩余参数,它包含了所有 extra 附加的传入参数,... 叫做 Spread operator 延伸符。

只有后面的传入参数才可能被标记为 rest parameter,如果调用时没有 extra 附加的传入参数,则 rest parameter 的值将会是一个空数组[],而不是 undefined

Spread Operator 延伸符

Spread Operator 延伸符类似于 Rest Parameter,但它在应用于 objectarrayfunction 时有其他功能。

function

在 ES6之前,我们可以通过 apply() method 来实现调用 function 时使用数组作为传入参数:

function test(a, b, c) {
    console.log(a + b + c);
}
var arr = [1, 2, 3];
test.apply(null, arr);

//output:
//6

通过 function 的 apply() method,可以将 method 应用于其他外部 object 上,同时用一个数组传入 function 参数。具体参考:JavaScript 入门教程之四 -- Functions

ES6 使用 Spread Operator 实现更加简单的语法来实现上面的功能:

const test = (a, b, c, d) => {
    console.log(a + b + c + d);
}
var arr = [1, 2, 3];
test(...arr, 4);

//output:
//10

以上示例中,我们使用了 ... 延伸符来将数组数据作为传入参数。因为数组只有 3 个数据,所以我们后面又附加了一个传入参数。

也可以应用于 constructor function:

let date = [2020, 11, 1];
let x = new Date(...date);
console.log(x.getFullYear());

array

ES6 之前,我们使用下面方法给数组中插入元素:

var arr = ["one", "two", "five"];
arr.splice(2, 0, "three", "four");
console.log(arr[3]);

splice method 用来给数组中某个位置插入数据,第一个参数是行号,第二个参数是列号,后面的参数是要插入的元素,可以是任意多个。

ES6 中,可以这样写:

let newArr = ["three", "four"];
let arr = ["one", "two", ...newArr, "five"];

因为数组的标准创建方法是:

let arr = new Array(a, b, c);

所以为了将传递参数放在数组中,我们就可以使用 ...延伸符来处理:

let arr = new Array(...newArr);
//or
let arr = [...newArr];

object literals

在 object 中使用 Spread Operator 可以复制所有的 properties 到新的 object:

const obj1 = {
    a: 1,
    b: 2
}
const obj2 = {
    c: 3,
    d: 4
}

const obj3 = {...obj1, ...obj2};
console.log(obj3.a);
console.log(obj3.d);

//output:
//1
//4

使用前面提到的 Object.assign() method 也可以实现复制 object。

注意,如果通过下面的方法想要合并两个 object 会得到不一样的结果:

const merge = (...objs) => ({...objs});

let newObj = merge(obj1, obj2);
console.log(newOb);

//output:
//{0: {a: 1, b: 2}
   1: {c: 3, d: 4}}

它会将每个 object 作为一个 property 的值。

Classes in ES6

这一节我们将介绍如何创建 class 来创建同样结构的不同 object。

使用关键词 class 来创建 class,包含一个 constructor method 来初始化参数。这个结构和 c++ 的 class 定义方法很类似。

下面是一个示例:

class Test {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
}

class 名称的首字母习惯用大写字符。声明好的 class 可以使用 new 关键词来创建 object

const x = new Test(1, 2);
const y = new Test(3, 4);
console.log(x.a);

//output:
//1

在使用 class 前必须先定义它,如果把定义放在调用后面,则会报错:ReferenceError。

也可以在表达式中直接定义 class,可以有 class 名称,也可以不写:

const x = class Test {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
};

const y = class {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
};

constructor 是 class 来初始化 object 时的特殊 method,每个 class 只能够一个 constructor

ES6 引入了一种简写语法,可以在定义 method 时省略 function 关键词。class 中定义的 method 叫做 prototype method,object 中可以调用对应 class 中的 method。

请看下面示例:

class Test {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
    get sum() {
        return this.calcSum();
    }
    calcSum() {
        return this.a + this.b;
    }
}

const x = new Test(1, 2);
console.log(x.sum);
console.log(x.calcSum());

//output:
//3
//3

以上代码中,sum 叫做 getter 获取器,calcSum 是 method。getter function 使用关键词 get 定义,必须有 return 返回值,调用时不需要写小括号(),类似于 property 的调用。

static method 是另一种特殊 method,这种 method 不能在实例化的 object 中调用,只能被 class 本身调用,使用关键词 static 定义:

class Test {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
    static distance(x, y) {
        const dx = x.a - y.a;
        const dy = x.b - y.b;
        return Math.hypot(dx, dy);
    }
}

const x = new Test(1, 2);
const y = new Test(3, 4);
let z = Test.distance(y, x);
console.log(z);

以上示例中可以看到,distance method 直接使用 Test class 调用,而不是 object。Static methods 常用于在一个 application 中创建功能性 function。

extends 关键词用来创建一个 class 的 child class。child class 继承了 parent class 的所有 properties 和 methods。

示例:

class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    speak() {
        console.log(`${this.name} barks.`)
    }
}

let dog = new Dog('tom');
dog.speak();

//output:
//tom barks.

上面示例中,Dog class 是 Animal class 的 child class,继承了 Animal 的所有 properties 和 method。child class 也可以 overwrite 重写 parent class 的 method。

当 child class 也有 constructor 时,需要首先调用 super() 来初始化 parent class,然后才能使用 this 关键词。初始化参数要考虑 parent class 需要的参数:

class Dog extends Animal {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    speak() {
        console.log(`${this.name} barks.`)
    }
}

同样的,使用 super 关键词可以调用 parent 的 method,这在 child class 重写了某个 method 时可以使用这个方法调用 parent 原始的 method,下面是修改后的完整示例:

class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    speak() {
        super.speak();
        console.log(`${this.name} barks.`)
    }
}

let dog = new Dog('tom', 10);
dog.speak();
console.log(dog.age);

//output:
//tom makes a noise.
//tom barks.
//10

super.speak() 调用了 parent class 的 method。

ES6 Map

Map object 可以存储 key/value pairs 配对数据组。一个 key 或 value 可以是任何数据类型,例如 string,number,object,源数据等任何数据。

使用 new Map([iterable]) 来创建 Map object,iterable 可以是任何可以 iterable 的数据,但每个元素都需要是由 key/value 两个构成的 pairs 数组。可以看到 Map 和 object 和相似,Map 的单个元素和 object 的 property 结构类似。但使用 Map 由如下优点:

  • key 可以是任何数据类型,包括:function,object或其他
  • 可以获取 Map 的 size,也就是包含的元素个数
  • 可以直接在 Map 中 iterate
  • 添加和删除 key/value 组合时性能更强
let map = new Map([
    ['a', 1],
    ['b', 2]
]);
console.log(map.size);

//output:
//2

以上示例定义了一个 Map object。包含两个 pairs 配对,每个配对都是一个含有 2 个元素的数组。Mapsize property 返回包含的配对个数。

Map object 可用的 method:

  • set(key, value) 添加一个 pairs 配对,如果指定的 key 已经存在,则会替换对应的 value。
  • get(key) 获取包含指定 key 的配对的 value,如果不存在指定 key 的 pairs 配对,则返回 undefined
  • has(key) 查询是否存在包含指定 key 的配对,如果存在返回 true
  • delete(key) 删除包含指定 key 的配对,然后返回 true,如果不存在这个配对,则返回 false
  • clear() 删除所有 Map 中的 pairs 配对
  • keys() 返回一个 iterator 包含所有 pairs 配对中的 key
  • values() 返回一个 iterator 包含所有 pairs 配对中的 value
  • entries() 返回一个 iterator 包含所有 pairs 配对数组:[key, value]

示例:

let map = new Map();
map.set('a', 1).set('b', 2);

console.log(map.get('a'));
console.log(map.has('b'));

for (const iterator of map.entries()) {
    console.log(iterator[0] + " : " + iterator[1]);
}

//output:
//1
//true
//a : 1
//b : 2

以上示例演示了部分 Map method。

ES6 Set

Set object 可以用来存储 unique 唯一的 values,不允许有重复数据。

使用 new Set([iterable]) 语法来创建一个 Set object,iterable 是一个数组或任何其他可以 iterate 的数据:

let set = new Set([1, 3, 5, 1, 7, 5]);
console.log(set.size);

//output:
//4

size property 返回这个 Set object 包含数据个数。可以看到返回值为 4,这是因为重复的数据会被忽略。

可供使用的 method 如下:

  • add(value) 添加一个新数据到 Set
  • delete(value) 删除某个数据
  • has(value) 检查是否存在某个数据,如果存在则返回 true
  • clear() 清空 Set
  • values() 返回一个 iterator 包含所有 Set object 中的数据。

示例:

let set = new Set();
set.add(1).add(3).add(9).add(1);

console.log(set.has(3));
for (const iterator of set) {
    console.log(iterator);
}

//output:
//true
//1
//3
//9

以上示例介绍了 Set 部分 method 的用法。

Set 支持不同的数据类型,例如 1'1' 是不同的数据。甚至是 NaNundefined 也可以存储在 Set 中。

ES6 Promises

Promise 相比较于 setTimeout() method 是一种更好的异步编程方法。

通常想要延时执行某个 function,可以通过 setTimeout() 实现:

setTimeout(() => {
    console.log('second');
    setTimeout(() => {
        console.log('third');
    }, 3000);
}, 3000);
console.log('first');

//OUTPUT:
//frist
//second
//third

以上示例实现:先输出 first,延时 3 秒输出 second,然后再过 3 秒后输出 third。每个 function 只执行一次,如果要循环执行,可以使用 setInterval() 实现。

setTimeout method 可以实现异步执行动作,但是如果需要处理的任务很多,使用这种方法就会导致嵌套很复杂。

ES6 引入的 promise 可以解决这个问题,使用下面语法结构创建 promise

new Promise(function(resolve, reject) {
    if (success)
        resolve(result);
    else
        reject(Error('failure'));
});

Promise 包含有两个 function 参数,这两个参数定义了 promise 成功和失败两种情况下的回传数据。在 promise 中定义这两者的逻辑。示例中 resolvesuccess 为 true 时被调用它的回传数据是 result 变量的数据,reject 在 else 情况被调用它的回传数据是字符串failure

当 promise 定义后,它会在其他进程保持 pending 挂起状态等待 callback 调用。

那么 Promise 中需要的这两个回传数据在什么地方被使用呢?如果一个 object 的返回类型为 Promise ,则可以调用 then method,其中定义的两个 callback function 分别对应于 resolve 和 rejecct,传入数据分别来自他们在 promise 中的传入数据。

下面的示例中,定义一个返回值为 Promise 类型的 function,然后调用这个 function:

const asyncTest = (work) => {
    return new Promise((resolve, reject) => {
        if (work === "")
            reject(Error('nothing'));
        setTimeout(() => {
            resolve(work);
        }, 3000);
    });
};

asyncTest('second')
.then((result) => {
    console.log(result);
    return asyncTest('third');
},
(error) => console.log(error))
.then((result) => console.log(result),
(error) => console.log(error));

console.log('frist');

//OUTPUT:
//frist
//second
//third

以上示例和第一个示例效果一样,首先输出 frist,3 秒后输出 second,在经过 3 秒输出 third。同样的都是三个输出都是异步进行的。

以上语法相比之前的代码使逻辑关系更加清晰,Promise object 定义了调用两个 method 的原则,通过调用 then method 定义 2 个 method 的指令,只有 Promise 类型的 object 才有 then method。所以如果要继续扩充异步动作,只需要在某个 method 中返回 promise 类型的数据,就可以继续通过 then method 定义其他动作。

以上示例中,我们通过第一个 then 定义 1 个延时触发指令,然后返回一个新的 Promise 类型 object,这样可以继续调用第二个 then 来实现其他异步动作。以此类推可以继续扩展下去。

也可以使用 thencatch method 分别定义 resolve 和 reject 情况的 callback function,如:

   asyncTest('second')
   .then(result => {
       console.log(result);
       return asyncTest('third');
   })
   .then(result => console.log(result))
   .catch(error => console.log(error))

   console.log('frist');

reject 情况时,会查找最近的一个 catch function。

Iterators & Generators

Symbol.iterator 是 object 内置默认的 iterator,for...of loop 就是基于这种类型的 iterator 来处理数组 object 的。

下面的示例介绍 Symbol.iteratorgenerator functions 如何使用:

let myIterableObj = {
    [Symbol.iterator]: function* () {
        yield 1;
        yield 2;
        yield 3;
    }
};
console.log([...myIterableObj])

以上示例中,我们创建了一个 object 通过 Symbol.iteratorgenerator function 来定义一些 properties。

使用关键词 function 后加一个星号* 叫做 generator function (or gen function)。

通过下面示例介绍如何使用 generator function

function* id() {
    let index = 0;
    while (index < 5) {
        yield index++;
    }
}
let x = id();
console.log(x.next().value);
console.log(x.next().value);
console.log(x.next().value);

//output:
//0
//1
//2

可以看到当 generator function 返回数据后,如果条件依然满足,它就不会直接跳出 function 而是会继续执行指令直到内部指令完全执行完毕,这一过程中可以使用关键词 yield 多次返回数据。这在实现异步程序中很有用,尤其是结合 Promise 来使用。

generator function 可以进行多级嵌套使用,实现更加复杂的循环逻辑。

下面的示例依然结合 Symbol.iteratorgenerator functions 使用:

const arr = ['a', '1', '3', '5b', '9', 'f'];
const myObj = {
    [Symbol.iterator]: function*() {
        for (const iterator of arr) {
            yield `${iterator}`;
        }
    }
};

const all = [...myObj]
.map(i => parseInt(i, 10))
.map(Math.sqrt)
.filter(i => i < 5)
.reduce((i, d) => i + d);

console.log(all);

//output:
//7.9681187850686666

以上示例的过程如下:

  • 首先我们创建一个数组 arr,数组元素都是字符或字符串类型。
  • 然后使用 Symbol.iteratorgenerator functions 的语法来创建一个 iterable object:myObj,数据使用前面定义的数组 arr。
  • 然后创建一个新的 all 数组,数据使用第二部创建的 myObj,由于 myObj 也是 iterable objet 所以这里通过 ... 延伸符来将 myObj 作为传入参数来创建数组 object。
  • 然后使用数组的 map method 来将字符元素转换成 int 数字,这里会过滤掉一些不能转换的元素。
  • 然后使用数组的 filter method 来进一步过滤元素
  • 最后使用数字的 reduce method 来将所有元素进行计算,返回为一个数字。

parseInt() Function 可以将字符解析返回为一个数字。语法如下:

parseInt(string, radix)

string 是需要解析的字符,radix 定义所使用的进制系统,可定义范围为:2-36,这是一个可选项,默认为 10。如果字符的首字符不是数字,则会返回 NaN

map,filter 和 reduce method 的用法在 JavaScript 入门教程之六 -- JS内置 Objects 做过介绍。

更多 array 的 method 用法参考:JavaScript Array Reference

Modules 模组 import

将相关的代码放在一个模块里有利于代码整洁度和可操作性,ES6 之前使用一些第三方库可以实现这一功能如:RequireJS, CommonJS,ES6 现在原生支持这一特性。

使用 modules 模组需要考虑的:

  • 可操作性,一个模组应该是完全独立的,不依赖于其他模组
  • 命名空间 namespacing,前面介绍了变量申明的 scope,使用 var 会在代码中全局申明,可能引起命名污染,使用模组可以创建一个私有空间来完美解决这个问题
  • 代码复用性,我们希望一个代码段能够被不同的项目引用,使用模组可以方便了在不同项目中调用

下面介绍如何创建和使用 modules,我们在项目 lib 目录下创建 math.js 文件:

export const sum = (x, y) => x + y;
export let pi = 3.14;

调用这个模块:

<script type="module">
    import * as math from './lib/math.js'
    console.log(math.sum(math.pi, math.pi));
</script>

//output:
//6.28

我们首先创建了一个 js 文件作为 module 模组,使用 export 关键词定义的 function 和 variable 变量可以被调用这个模组的文件访问到。

然后在我们的 script 中使用 import 关键词来调用模组。注意如果 script 中调用了其他模组,需要设置是script type 类型为 module,否则浏览器会报错。

import 时我们设置了这个 module 的名称为 math,module 中的 function 或 variable 时就可以通过 math 来调用,就像 object 一样。

也可以指定调用模组里的某个元素:

    import sum from './lib/math.js'
    console.log(sum(2, 2));

此时调用的名称必须和模组里的名称吻合,后续就可以在程序中直接使用 sum 来表示 math.js 中的 sum function。

也可以同时调用模组里的多个元素,使用大括号{} 定义:

    import {sum, pi} from './lib/math.js'
    console.log(sum(2, 2));
    console.log(pi);

建议使用此种方式调用需要的元素,避免将整个模组调入系统占用资源,同时也使程序结构更加清晰。

需要注意的是,如果你是在本地开发,直接打开包含调用了 module 的 html 文件或 js 文件会报错,提示:Access to Script at ' from origin 'null' has been blocked by CORS policy,需要通过真实的搭建一个 web 服务器来访问这个 html 文件才能正常执行。

如果使用 chrome 可以通过插件来建立一个简单的本地 web 服务器:Web Server for Chrome

Built-in Methods 内建 methods

ES6 引入了一些新的内建 method 使有些任务可以更轻松地执行,下面介绍最常用的几种。

Array Element Finding 数组元素查询

ES6 之前我们想要得到一个数组经过某个规则过滤后的第一个元素,可以通过 filter method 实现:

var x = [1, 4, 6, 3, 8].filter(function(i) {
    return i > 5;
})[0];

console.log(x);

//output:
//6

现在我们可以使用 find method 更加简洁的语法实现相同的功能:

var x = [1, 4, 6, 3, 8].find(i => i > 5);
console.log(x);

//output:
//6

也可以使用 findIndex method 来得到符合条件的第一个元素的索引地址:

var index = [1, 4, 6, 3, 8].findIndex(i => i > 5);
console.log(index);

//output:
//2

Repeating Strings 重复字符串

ES6 之前,想要将一个字符串重复多次可以使用一下方法:

console.log(Array(4).join('foo'));

//output:
//foofoofoo

以上示例中,首先声明一个有 4 个元素 array 数组但没有赋初值,当前元素为 empty,然后通过 join method 给将所有元素合并为字符串,分隔符为 foo,这样我们就变相返回了一个将源字符串重复三次的新字符串。

使用 ES6 的新语法实现这个功能更加简单:

console.log('foo'.repeat(3));

//output:
//foofoofoo

字符串的 repeat method 的参数定义重复次数并返回新字符串。

Searching Strings 查询字符串

ES6 之前我们只能通过 indexOf method 来查询一个 string 中 text 的位置,如:

var x = 'helloworld'.indexOf('llo') === 2;
var y = 'helloworld'.indexOf('llo', 3) === -1;
console.log(x);
console.log(y);

//output:
//true
//true

关于 indexOf 的用法参考:JavaScript 入门教程之六 -- JS内置 Objects

ES6 使用更加简洁明了的语法代替以上语法:

var x = 'helloworld'.startsWith('hel', 0);
var y = 'helloworld'.endsWith('hello', 5);
var z = 'helloworld'.includes('llo');
var i = 'helloworld'.includes('llo', 3);

console.log(x);
console.log(y);
console.log(z);
console.log(i);

//output:
//true
//true
//true
//false

startsWith method 返回所查询的 text 的起始字符所在位置。
endsWith method 返回所查询的 text 结束后紧跟的元素的所在位置。
includes mehod 返回所查询的 text 的所在位置,默认从第 0 位开始,可自定义起始查询位置。

如果不存在所查询的元素,将返回 -1

以上就是 ES6 中较常用的新语法,这里我推荐在实现同一功能的情况下,优先使用 ES6 新引入的语法来处理。这样可以使我们的代码更加简洁易读。

经过 8 个章节的教程,我们基本涵盖了 JavaScript 的基本编程方法。后续的学习中需要继续补充完善,在实际应用中才能对 JavaScript 有进一步的认识。

]]>
1 https://blog.niekun.net/archives/2011.html#comments https://blog.niekun.net/feed/archives/2011.html
JavaScript 入门教程之七 -- DOM https://blog.niekun.net/archives/2002.html https://blog.niekun.net/archives/2002.html Sat, 19 Dec 2020 14:10:00 +0800 admin 当你打开一个网页,html 页面会被加载和渲染到屏幕上。为了完成这个过程,浏览器会建立这个页面的 Document Object Model 模型。也就是一个指向页面逻辑架构的 object。

一个页面的 DOM 可以被表示为一些嵌套的 boxes:
1.png

JavaScript 能够用来操作 DOM,动态的添加、删除、修改其中的元素。

DOM tree

DOM 将一个 document 表达为一个 tree structure 树形结构, html 元素称为这个 tree 上的相关联的 nodes 节点。

整个 tree 上的 nodes 之间都是互相有关联的。nodes 可以有 child node 子节点。在同一个 tree level 层级的 nodes 叫做 siblings 兄弟关系。想象下面的一个 document structure:
2.png

以上示例中的关系结构是:

  • <html> 有两个 children:<head>, <body>;
  • <head> 有一个 child:<title>,有一个 parent:<html>;
  • <title> 有一个 parent:<head>, 没有 children;
  • <body> 有两个 children:<h1> and <a>,有一个 parent:<html>;

理解 HTML document 的元素间的关系很重要,这样我们就可以使用 JavaScript 来操纵这些元素了。

document object 在 JavaScript 中被预定义,可以用来访问 DOM 中的所有元素。换句话说,document object 是页面中所有元素的拥有者。所以想要访问 html 页面中的元素,首先需要访问 document object。

看下面的示例:

document.body.innerHTML = "Some text";

body 是 DOM 中的元素,通过 document 来访问到它,然后通过其 innerHTML property 修改其内容。

Selecting Elements

所有的元素都是 object,都有 properties 和 method。document 有能够让我们选择其内部元素的 method,常用的有三种方法:

//finds element by id
document.getElementById(id) 

//finds elements by class name
document.getElementsByClassName(name) 

//finds elements by tag name
document.getElementsByTagName(name)

下面的示例,我们使用 getElementById method 来选中一个元素,并修改其内容:

var elem = document.getElementById("demo");
elem.innerHTML = "hello world";

注意以上示例中我们需要 html 有一个元素设置 id="demo",如:

<body>
    <div id="demo"></div>
</body>

getElementsByClassName() method 会返回一个特定 calss name 的集合。例如,我们有三个含有 class="demo" 的元素:

<body>
    <div class="demo">test1</div>
    <div class="demo">test2</div>
    <div class="demo">test3</div>
</body>

首先选中全部 3 个元素为一个数组,然后修改第一个元素内容:

<script>
    var arr = document.getElementsByClassName("demo");
    arr[0].innerHTML = "hi";
</script>

类似的方式, getElementsByTagName method 返回一个特定 tag 的集合。下面的示例中,将选中所有 paragraph 元素然后修改他们的内容:

<body>
    <p>1</p>
    <p>2</p>
    <p>3</p>
</body>

<script>
    var arr = document.getElementsByTagName("p");
    for (var i = 0; i < arr.length; i++) {
        arr[i].innerHTML = "hi there";
    }
</script>

每个 DOM 中的元素都有 properties 和 method 来提供给我们关于其在 DOM 中的相互关系的信息:

  • element.childNodes 返回其子元素的数组
  • element.firstChild 返回其第一个子元素
  • element.lastChild 返回其最后一个子元素
  • element.hasChildNodes 当其有子元素时返回 true,否则返回 false
  • element.nextSibling 返回下一个处于同一 tree level 节点的元素
  • element.previousSibling 返回上一个处于同一 tree level 节点的元素
  • element.parentNode 返回其 parent 节点的元素

Changing Attributes

当选中了想要的元素后,就可以修改其相关属性了。

我们前面使用过通过 innerHTML property 修改了元素的 text 内容。同样的方法,我们可以修改它的属性值,例如修改一个 image 的 src 属性:

</body>
    <img id="img1" src="1.jpg" alt="" srcset="">
</body>

<script>
    var el = document.getElementById("img1");
    el.src = "2.jpg";
</script>

通常情况下,元素中所有的属性都可以通过 JavaScript 修改。

html 元素的 style 也可以通过 JavaScript 修改,所有的 style 属性可以通过 style object 来访问,例如:

<body>
    <div id="demo2" style="width: 200px;">some text</div>
</body>

<script>
    var x = document.getElementById("demo2");
    x.style.color = '#6600FF';
    x.style.width = '100px';
</script>

所有的 css properties 都可以通过 JavaScript 修改。但是要注意,JavaScript 中相关 property 时,名称中不能够使用 dash- 横杠,如果 css 属性名称有横杠,需要转换成 camelCase versions,也就是相关首字母转换成大写。例如:js 在调用 background-color property 时需要写成 backgroundColor

Adding & Removing Elements 创建和删除元素

Adding Elements

使用下面的 method 来创建节点 node:

  • element.cloneNode() clone 一个节点并将其返回
  • document.createElement(element) 创建一个新元素的节点
  • document.createTextNode(text) 创建一个新的 text 节点

下面简单说下元素和节点:

<div>
    test1
    <p></p>
</div>

以上示例中,div 就是一个 element 元素,test1 就是这个元素下的 text node 节点,p 就是 div 元素下的另一个子元素节点。

例如:

var node = document.createTextNode("Some new text");

将会创建一个 text 节点,但是当前它并不会出现在页面上,因为还没有定义它属于那个元素。

element.appendChild(newNode) method 来给元素添加一个新的子节点,并放在最后。

element.insertBefore(node1, node2) method 将添加的新直接点 node1 放在已有的子节点 node2 之前。

下面做一个示例:

<body>
    <div id="demo3">test</div>
</body>

<script>
    var p = document.createElement("p");
    var node = document.createTextNode("some text");
    p.appendChild(node);

    var el = document.getElementById("demo3");
    el.appendChild(p);
</script>

以上出现的执行过程为:创建一个元素 p 和 text node node,然后将 node 作为 p 的子元素,最后将 p 添加到 el 中。

Removing Elements

想要删除一个元素,首先需要选中其 parent 元素,然后使用 removeChild(node) method 来删除相关子元素,例如:

<body>
    <div id="div1">
        <p id="p1">some text one</p>
        <p id="p2">some text two</p>
    </div>
</body>

<script>
    var parent = document.getElementById("div1");
    var child = document.getElementById("p1");
    parent.removeChild(child);
</script>

也可以使用 parentNode property 来获取某个子元素的 parent 元素,然后执行相关操作:

child.parentElement.removeChild(child);

Replacing Elements

使用 element.replaceChild(newNode, oldNode) method 来替换一个元素。例如:

<script>
    var newEl = document.createElement("p");
    var node = document.createTextNode("this is a new node");
    newEl.appendChild(node);

    var parent = document.getElementById("div1");
    var child = document.getElementById("p1");
    parent.replaceChild(newEl, child);
</script>

Animations 动态效果

现在我们已经知道如何选择和修改元素,下面我们可以创建一个简单的动画效果。

首先建立一个 html 页面,包含一个 box 元素,后期通过 js 来让它动起来:

<style>
    #container {
        width: 200px;
        height: 200px;
        background: green;
        position: relative;
    }
    #box {
        width: 50px;
        height: 50px;
        background: red;
        position: absolute;
    }
</style>
<body>
    <div id="container">
        <div id="box"></div>
    </div>
</body>

box 元素是 container 的子元素,注意两个的 position attribute,container 是 relative,box 是 absolute,我们将创建一个让 box 从左移动到右的动画。

为了实现动态效果,我们需要在一个很小的时间间隔下修改元素的相关 properties,可以使用 setInterval() method:

<script>
    var pos = 0;
    var box = document.getElementById("box");

    function move() {
        pos++;
        box.style.left = pos + "px";
    }

    setInterval(move, 10);
</script>

以上指令控制 boxleft property 每 10 毫秒移动 1 位。

但是以上的代码会让 box 元素一直向右移动,我们可以加一个简单的判断来检测 box 到达 container 边沿,然后使用 clearInterval() method 停止定时器:

function move() {
    if (pos >= 150) {
        clearInterval();
    } else {
        pos++;
        box.style.left = pos + "px";
    }
}

left 属性达到 150 时,由于 box 宽度为 50,container 宽度为 200,这时候 box 已经达到边沿。

Handling Events 事件处理

我们可以实现当一个 event 事件发生时执行特定 JavaScript 代码,如点击某个元素,移动鼠标,提交一个表格等。

当一个 event 发生在目标元素上时,一个 handling function 会被执行。常用的 html events 包括:
3.png

event 事件可以作为一个属性添加在元素内,如:

 <p onclick="someFunc()">some text</p>

下面我们创建一个事件,当用户点击元素时弹出一个窗口:

<body>
    <button onclick="show()">click me</button>
</body>

<script>
    function show() {
        alert("hello");
    }
</script>

event handling 事件响应也可以在 js 中直接分配给 elements:

<body>
    <div id="demo">demo</div>
</body>

<script>
    var el = document.getElementById("demo");
    el.onclick = function () {
        el.innerHTML = "clicked";
    }
</script>

onloadonunload events 会在用户进入和离开页面时被触发。可以用来实现当页面加载完成后执行的动作:

<body onload="doSomething()">

类似的 window.onload event 可以在整个页面加载后执行动作:

window.onload = function () {
    //do someting
}

onchange event 在文本框中很有用,当文本框的 text 内容被修改同时元素不在 focus 状态时 onchange event 响应。

例如:

<body>
    <input type="text" id="name" onchange="changed()">
</body>

<script>
    function changed() {
        var el = document.getElementById("name");
        el.value = el.value.toUpperCase();
    }
</script>

以上示例中,当我们在文本框输入字符,然后将光标移动到其他地方或者敲回车键后,文本框字符会自动转换为大写。

理解 events 很重要,因为它是实现动态页面的核心。

Event Listeners

addEventListener() method 可以给一个元素添加 event handler 而不会覆盖其已有的 event handlers。可以给一个元素添加多个 event handler 甚至是统一类型的 handler,比如可以添加两个 click handler。

语法如下:

element.addEventListener(event, function, useCapture);
  • 第一个参数是 event 类型,如:"click" 或 "mousedown",需要用引号包围
  • 第二个参数是事件发生时调用的 function 名称,不需要写小括号()
  • 第三个参数时一个 Boolean 数值,定义是使用 event bubbling 还是 event capturing,这个参数是可选项,后续会介绍

注意这里 event 名称不需要 on 前缀,使用 click 代替 onclick

element.addEventListener("click", myFunction);
element.addEventListener("mouseover", myFunction);

function myFunction() {
  alert("Hello World!");
}

以上代码给元素添加了两个 event handler,我们可以删除其中一个:

element.removeEventListener("mouseover", myFunction);

下面的示例中,我们给 button 创建一个 event handler,然后再触发事件后删除这个 event handler:

<body>
    <button id="btn">click me</button>
</body>

<script>
    var btn = document.getElementById("btn");
    btn.addEventListener("click", myFunction);

    function myFunction() {
        alert(Math.random());
        btn.removeEventListener("click", myFunction);
    }
</script>

当第一次点击 button 后会弹出窗口,然后删除 click event,后面点击会没有反应。

IE8 及以下版本的浏览器不支持 addEventListener()removeEventListener() methods,可以使用 document.attachEvent() 来添加 event handler。

Event Propagation 事件传播

在 DOM 中有两种方式进行 event propagation 事件传播:bubblingcapturing

当事件触发时,可以定义元素顺序。例如有一个 <p> 元素在 <div> 元素内部,当用户点击 <p> 元素时,哪个元素的 click event handler 首先被触发?

  • bubbling 模式下,最内部元素的 event 最先响应,逐级触发外层元素;
  • capturing 模式下,最外部元素的 event 最先响应,逐级触发内层元素。

addEventListener() method 支持设置事件传播类型,定义下面的 useCapture 参数:

addEventListener(event, function, useCapture)

useCapture 默认值为 false,也就是 bubbling 模式,当设置为 true 时 event 使用 capturing 模式。例如:

//Capturing propagation
elem1.addEventListener("click", myFunction, true); 

//Bubbling propagation
elem2.addEventListener("click", myFunction, false);

当同一个 event 存在于多个 DOM 层级中的元素时,设置 event propagation 事件传播模式很有用。

Image Slider 幻灯片

下面我们制作一个幻灯片程序,通过 NextPrev button 来切换图片。

首先创建 html,包含两个 button 和一个 image:

<html>
<body>
    <button> Prev </button>
    <img id="slider" src="https://blog.niekun.net/usr/uploads/2020/12/2749019084.jpg">
    <button> Next </button>
</body>
</html>

然后再 JavaScript 中定义我们的图片数组:

<script>
    var images = [
    "https://blog.niekun.net/usr/uploads/2020/12/2749019084.jpg",
    "https://blog.niekun.net/usr/uploads/2020/12/1648461111.jpg",
    "https://blog.niekun.net/usr/uploads/2020/12/3914156262.jpg"];
</script>

下面需要添加响应 NextPrev button 的 event handler 来切换不同图片:

<body>
    <button onclick="prev()"> Prev </button>
    <img id="slider" src="https://blog.niekun.net/usr/uploads/2020/12/2749019084.jpg">
    <button onclick="next()"> Next </button>
</body>
<script>
    var images = [
    "https://blog.niekun.net/usr/uploads/2020/12/2749019084.jpg",
    "https://blog.niekun.net/usr/uploads/2020/12/1648461111.jpg",
    "https://blog.niekun.net/usr/uploads/2020/12/3914156262.jpg"];

    var num = 0;
    function next() {
        var slider = document.getElementById("slider");
        num++;
        if (num >= images.length)
            num = 0;
        slider.src = images[num];
    }

    function prev() {
        var slider = document.getElementById("slider");
        num--;
        if (num < 0)
            num = images.length - 1;
        slider.src = images[num];
    }
</script>

效果如下:
4.jpg

num 变量存储当前图片的 index 索引。

Form Validation 表格验证

html5 添加了一些用来验证的属性,例如 required attribute 能够添加给 input 元素来强制必须输入内容。

更加复杂的验证机制可以通过 JavaScript 完成。

form 元素有一个 onsubmit event 能够用来进行验证。我们创建一个 form 表格,有两个 input 和一个 button,需要两个 input 输入一样的内容且不为空才通过验证:

<form onsubmit="return validate()" method="post">
    number: <input type="text" name="num1" id="num1">
    <br>
    repeat: <input type="text" name="num2" id="num2">
    <br>
    <input type="submit" value="submit">
</form>

然后定义 validate() function:

<script>
    function validate() {
        var n1 = document.getElementById("num1");
        var n2 = document.getElementById("num2");

        if (n1.value != "" && n2.value != "") {
            if (n1.value == n2.value)
                return true;
        }
        alert("the values shoud be equal and not blank");
        return false;
    }
</script>

只有当 onsubmit event 的返回值为 true 时,form 才会被提交。

]]>
0 https://blog.niekun.net/archives/2002.html#comments https://blog.niekun.net/feed/archives/2002.html
JavaScript 入门教程之六 -- JS内置 Objects https://blog.niekun.net/archives/1997.html https://blog.niekun.net/archives/1997.html Fri, 18 Dec 2020 18:53:00 +0800 admin 下面介绍一些 JavaScript 内部定义好的 obect,可以直接使用。

JavaScript Arrays 数组

当你需要定义三个课程名称时,需要分别定义:

var course1 ="HTML"; 
var course2 ="CSS"; 
var course3 ="JS"; 

当你有 100 个课程名称呢?这时候可以使用 Array:

var courses = new Array("HTML", "CSS", "JS");

以上定义了一个数组 courses,存储了 3 个元素。

可以使用 index 索引号内访问数组元素,索引号 0 表示第一个元素:

var courses = new Array("HTML", "CSS", "JS");
var course = courses[0];
courses[1] = "c++";

以上示例表示:给变量 course 赋值数组第一个元素,给第二个元素赋值 c++

如果尝试访问超出数组元素的索引,会返回 undefined

在定义数组时也可以只申明数组元素个数,后续再给元素赋值:

var courses = new Array(3);
courses[0] = "HTML";
courses[1] = "CSS";
courses[2] = "JS";

array 数组是一种特殊的 object,它使用 index number 来访问元素,而标准 object 使用 property name 来访问元素,name 是字符形式。

如下是 array 和 object 的元素定义区别:

var arr = {
    0: 0,
    1: 1,
    2: 2,
    3: 3
}

var obj = {
    '0': 0,
    '1': 1,
    '2': 2,
    '3': 3
}

在 array 的 index 数字索引永远是按顺序排列的。所以我们访问以上示例中 arr 和 obj 元素的方式为:arr[0], obj['0']

JavaScript array 是动态的,也就是你可以在创建时不传入任何参数给构造器:

var courses = new Array();
courses[0] = "HTML";
courses[1] = "CSS";
courses[2] = "JS";
courses[3] = "C++";

你可以添加任意多个元素给数组。

为了定义更加方便,可以使用 array literal 语法来创建数组:

var courses = ["HTML", "CSS", "JS"];

以上语法和使用 new 关键词创建的数组是一样的。推荐使用这种语法

JavaScript array 内建了很多实用的 properties 和 method。

length property 返回数组的元素个数:

var courses = ["HTML", "CSS", "JS"];
document.write(courses.length);

length 的返回值比最后一个元素的索引号大 1,如果数组为空,则返回值为 0。

concat method 可以叠加两个数组并返回为一个新的数组:

var c1 = ["HTML", "CSS", "JS"];
var c2 = ["C++"];
var newC = c1.concat(c2);

newC 数组拥有四个元素:"HTML", "CSS", "JS", "C++"。注意使用 concat() method 并不会影响 c1 和 c2。

map method 可以给数组每个元素调用一个 function 然后返回修改后的数据为一个新数组:

var arr = [1, 2, 3, 4];
var newArr = arr.map(function(i) {
    return i * 2;
})
console.log(newArr);

//output:
//0: 2
//1: 4
//2: 6
//3: 8
//length: 4

reduce Method 可以将数组元素合并为一个 value。语法为:

array.reduce(function(total, currentValue))

total 为元素合并的累积值,currentValue 为当前循环处理到的元素。调用 reduce 时,第一次传入 total 为 第一个元素数据,currentValue 为数组第二个元素,function 会返回一个结果作为第二次循环 total 的数据,currentValue 指向第三个元素,然后进行循环调用 function 累积 total 的值,最后返回 total。

下面举例说明:

var arr = [1, 3 ,5];
var sum = arr.reduce(function(total, curr) {
    return total + curr;
});
console.log(sum);

//output:
//9

filter method 可以逐个过滤数组的元素,返回符合条件元素创建一个新数组。语法如下:

array.filter(function(currentValue))

filter 将数组元素逐个作为传入参数调用 function,function 返回值为 Boolean 类型,如果为 true 则返回当前数组元素,反之则丢弃当前数组元素。

示例如下:

var arr = [1, 4, 6, 3, 8];
var newArr = arr.filter(function(i) {
    return i < 5;
});

console.log(newArr);

//OUTPUT:
//0: 1
//1: 4
//2: 3
//length: 3

join method 将数组所有元素转换为一个字符串并返回这个字符串。下面是一个示例:

var arr = ['a', 'b', 'c'];
var x = arr.join();
console.log(x);

//output:
//a,b,c

元素间会通过一个分隔符隔开,默认是一个逗号,,可以通过 join 的参数自定义分隔符:

var arr = ['a', 'b', 'c'];
var x = arr.join('/');
console.log(x);

//output:
//a/b/c

indexOf method 搜索一个数组中特定的元素,并返回这个元素所在 index 索引。语法如下:

array.indexOf(item, start)

item 是搜索的元素,start 定义开始搜索的位置,默认为 0。

示例如下:

var arr = ['a', 'b', 'c', 'a', 'e', 'b'];
var x = arr.indexOf('b');
var y = arr.indexOf('b', 2);
var z = arr.indexOf('f');
console.log(x);
console.log(y);
console.log(z);

//output:
//1
//5
//-1

当搜索的元素不存在时,返回值为 -1

Associative Arrays 关联型数组

很多编程语言支持给数组添加命名化的 index 索引,也就是给每个元素定义一个名称,但是 JavaScript 不支持这种操作,因为它只能使用内部默认的 index 数字型索引。

但是我们依然可以定义命名化的 index 索引数组,JavaScript 会将其作为一个 object 处理:

var a = [];
a["name"] = "marco";
a["age"] = 20;
document.write(a["age"]);

JavaScript 会将 a 作为 object 处理,这样 name 和 age 就是其 properties。可以使用以上写法来读取 property 数据。

由于 a 数组被作为 object 处理,所以标准 array 的一些 method 和 properties 将无法正确执行,例如:a.length 返回值将是 0 而不是 2。

JavaScript 原生并不支持命名化的元素索引,所以推荐当你想要使用 number 数字型 index 时使用 array,当你想要使用命名化的索引时使用 object。

the Math Object 数学对象

the Math Object 可以用来处理数学运算任务,它包含多个 properties:
1.png

Math 没有 constructor 构造器,所以使用时并不需要单独创建 object。例如:

document.write(Math.PI);

以上将输出:3.141592653589793

Math object 包含多个 method 用来计算:
2.png

下面示例计算 4 的平方根:

var a = Math.sqrt(4);

如果想要得到一个 0- 10 的随机数,可以使用下面方法:

Math.ceil(Math.random() * 10);

下面编写一个小程序,让用户输入一个数字然后将这个数字的平方根放在弹窗通知中:

var n = prompt("please input a number: ", "");
var m = Math.sqrt(n);
alert(m);

The Date Object 时间对象

setInterval() method 用来在指定的间隔时间(毫秒)下调用 function 或评估一个表达式。它将会持续调用直到执行 clearInterval() 或关闭窗口。

下面的示例将会每三秒钟弹出窗口:

function myAlert() {
    alert("hello");
}
setInterval(myAlert, 3000);

注意传递 function 时只需要写 function 名称即可,不需要小括号()

Data object 可以让我们使用时间元素,一个 Date object 由:a year, a month, a day, an hour, a minute, a second, and milliseconds 构成。

使用 new 关键词来创建一个 Date obect,包含有当前的日期和时间

var d = new Date();
//d stores the current date and time

也可以使用指定的日期和时间来创建 Date object:

new Date(milliseconds)
new Date(dateString)
new Date(year, month, day, hours, minutes, seconds, milliseconds)

JavaScript 日期计算使用毫秒为单位,起始日期为:01 January, 1970 00:00:00 (UTC)。一天包含 86,400,000 毫秒。

以下示例使用不同方式定义指定日期:

//Fri Jan 02 1970 00:00:00
var d1 = new Date(86400000); 

//Fri Jan 02 2015 10:42:00
var d2 = new Date("January 2, 2015 10:42:00");

//Sat Jun 11 1988 11:42:00
var d3 = new Date(88,5,11,11,42,0,0);

JavaScript 的月份从 0 到 11,1 月就是 0,12 月就是 11。Date object 是 static 类型的,创建后就不会改变。

Date object 有如下 method 可供使用:
3.png

例如读取当前小时数值:

var d = new Date();
document.write(d.getHours());

下面示例在窗口显示当前时间,且每秒刷新一次:

function printTime() {
    var d = new Date();
    var hour = d.getHours();
    var min = d.getMinutes();
    var sec = d.getSeconds();
    document.body.innerHTML = hour + ":" + min + ":" + sec;
}

setInterval(printTime, 1000);

innerHTML property 可以设置或返回一个 HTML 元素的内容,这里我们将 document 的 body 块的内容赋值为我们定义的时间数据,并且每秒覆盖更新一次。

]]>
0 https://blog.niekun.net/archives/1997.html#comments https://blog.niekun.net/feed/archives/1997.html
JavaScript 入门教程之五 -- Objects https://blog.niekun.net/archives/1996.html https://blog.niekun.net/archives/1996.html Fri, 18 Dec 2020 15:25:00 +0800 admin JavaScript 的 variables 变量是用来包含数据的容器。object 同样是变量,但是包含了多个数据。

一个 object 是一组使用 name:value 模式定义的数据集合,大括号{} 内部定义数据,数据间使用逗号, 分隔,注意大括号结尾的分号;

var person = {
    name: "marco", age: 20,
    like: "basketball", height: 170
};

这些数据叫做 properties

propertyproperty value
namemarco
age20
likebasketball
height170

object 初始化语法可以写在一行,也可以写在多行。以下两种写法都是一样的:

var John = {name: "John", age: 25};

var John = {
  name: "John",
  age: 25
};

JavaScript 的 object 就是一组有命名的数据的容器。

有两种方法访问 object 的 properties:

objectName.propertyName
//or
objectName['propertyName']

使用上面的示例访问 properties,以下两种结果是一样的:

var x = person.age;
var y = person['age'];

使用 JavaScript 内建的 length property 可以得到 property 或 string 包含的字符个数:

var a = person.name.length;

method

object method 就是一个定义了 function 的 property,使用 object method 的语法为:

objectName.methodName()

我们前面多次使用 document.write() 来输出内容到网页,实际上 write() function 就是 document object 的一个 method。

定义一个 method 的语法如下:

methodName = function() { code lines }

在上面的示例中给 object 加入一个 method:

var person = {
    name: "marco", age: 20,
    like: "basketball", height: 62,
    test: function() {
        alert("method");
    }
};

person.test();

使用 method 的方法和 properties 一样,需要加上小括号()

The Object Constructor 构造器

上一节我们学习了如何建立 object,例如:

var person = {
name: "John", age: 42, favColor: "green"
};

以上的写法一次只能创建一个 object,那么如何设置一种 object type 用来创建多个统一类型的 objects 呢?

标准的方法是使用 constructor function 构造器来定义一个 object type

function person(name, age, color) {
  this.name = name;
  this.age = age;
  this.favColor = color;
}

以上就是一个 constructor function,接收传入数据来赋值给 object properties。关键词 this 表示当前 object 本身。

当我们定义了一个 object constructor,就可以使用关键词 new 来新建一个 object:

var p1 = new person("marco", 25, "blue");
document.write(p1.name);

//output:
//marco

p1 就是 person 类型的 object,它的 properties 就是对应传入的数据。

同样的方法,可以在 object constructor 中定义 method:

    function person(name, age, color) {
        this.name = name;
        this.age = age;
        this.favColor = color;
        this.changeName = function(name) {
            this.name = name;
        }
    }

    var p1 = new person("marco", 25, "blue");
    p1.changeName("john");
    document.write(p1.name);

以上示例中,我们定义了一个 changeName method,它有一个参数 name 用来赋值给 object 的 propertiy name

也可以在 object constructor function 外部定义 method:

function person(name, age, color) {
    this.name = name;
    this.age = age;
    this.yearOfBirth = bornyear;
}

function bornyear() {
    return 2020 - this.age;
}

var p1 = new person("marco", 25);
document.write(p1.yearOfBirth());

以上示例中,我们给 property yearOfBirth 赋值为 bornyearbornyear function 在外部定义,this 关键词可以来访问 person 的 properties,因为 bornyear 复制给了 person 的一个 property。

注意当将一个 function 赋值给 object 时不需要写小括号()

通过 object property name 来调用构造器定义的 method,注意不是外部 function 的名称。

]]>
0 https://blog.niekun.net/archives/1996.html#comments https://blog.niekun.net/feed/archives/1996.html
JavaScript 入门教程之四 -- Functions https://blog.niekun.net/archives/1992.html https://blog.niekun.net/archives/1992.html Fri, 18 Dec 2020 13:58:00 +0800 admin JavaScript function 就是一个执行特定任务的代码块。

使用 function 的优点是:

  • 代码复用
  • 通过传入不同参数得到不同结果

function 需要被调用才能执行。

定义

使用关键词 function 来定义一个 function,需要指定名称,使用大括号{} 来定义代码块:

function name() { 
  //code to be executed
}

function name 可以包含字符、数字,下划线和 & 符号。

下面定义一个简单的 function:

function myFunction() {
    alert("call the function");
}

myFunction();

一个 function 可以被调用任意次数,

当需要调用 function 时,直接使用 function name 名称及小括号() 即可。注意结尾要写分号;

还有一种调用写法:myFunction.call()。当使用这种方法时,会自动将关键词 this 传入此 function,后续会详细介绍。

function 参数

function 可以接收参数,在定义时需要列出来:

functionName(param1, param2, param3) {
   // some code
}

多个参数使用逗号, 隔开。

当定义了参数后,就可以在 function 代码块内使用:

function myFunction(name) {
    alert("hi " + name);
}

myFunction("marco");

以上第一了一个 function 有一个 name 参数,当调用此 function 时需要给 name 赋值。function 内部就会将赋值数据分配个 name 参数。

调用 function 时,通过不同的参数值来得到不同的结果:

myFunction("marco");
myFunction("john");
myFunction("tom");

使用逗号分隔多个参数:

function myFunction(name, age) {
    alert("my name is " + name + ", age is " + age);
}

myFunction("marco", 20);

JavaScript 不会检查调用 function 时的传入参数个数和定义的参数个数是否一致,如果调用时传入参数数量少于 function 定义参数个数,则缺失的参数会被赋值为 undefined,表示没有被赋值数据。如果传入参数数量大于 function 定义参数个数,可以使用数组:arguments 来访问这些传入数据,在 function 内可以被调用,形如:arguments[0], arguments[1],arguments 数组是 function 内自动定义的,按顺序存储所有的传入参数数据。

请看下面示例:

function test(a, b) {
    console.log(arguments[0]);
    console.log(arguments[1]);
    console.log(arguments[2]);
}
test(1, 2, 3);

//output:
//1
//2
//3

以上示例中,function 本身只定义了 2 个参数,但是我们在调用时传入了 3 个数据,这时候为了访问到第 3 个数据,就需要使用 arguments 数组来处理了。可以看到 function 内除了使用定义的参数名外,也可以使用内部自动定义的 arguments 数组来访问传入数据。

function 可以有一个可选的 return 命令,用来从 function 返回一个数据。

当 JavaScript 执行到 return 时,会停止执行后续指令。

下面的示例中,我们计算两个参数的和并返回结果:

function myFunction(a, b) {
    return a + b;
}

var x = myFunction(1, 2);

变量 x 的值就是 3。

如果 function 中没有 return 则默认会返回 undefined

Alert, Prompt, Confirm

JavaScript 提供三种弹出窗口:Alert, Prompt, Confirm。

alert Box 用来给用户显示一个提示信息,需要点击 OK 来取消弹窗。有一个参数:

alert("this is a alert");

信息中需要换行的话可以加入 \n

alert("this is\n a alert");

效果如下:
2.jpg

Prompt Box 用来弹出一个用户可以输入数据的提示窗口。

用户需要点击 OK 或 cancel 来退出窗口,如果点击 OK 则会 return 输入的数据,如果点击 cancel 则会返回 null。

prompt() 有两个参数,第一个是窗口显示的提示信息字符串,第二个是输入框的默认字符(可选项):

var a = prompt("please input value", "test");
document.write(a);

效果如下:
3.jpg

选择 OK 后变量 a 会赋值为输入的数据。

confirm box 可以用来作为确认对话框。

用户必须点击 OK 或 cancel 来退出窗口,当点击 OK 后 box 会返回 true,当点击 cancel 后 box 会返回 false:

var result = confirm("if confirm");

if (result) {
    alert("confirmed");
}
else {
    alert("not confirmd");
}

弹出窗口不要过多的使用,因为弹出窗口会导致页面不可用。

inline function

有一种特殊的 function 可以在直接在调用中进行定义,不需要写 function 名称。例如想要对 array 数组的每个元素执行一个 function,通过 forEach method 来给每个元素调用 function:

var arr = [1, 2, 3];
arr.forEach(function(el) {
    console.log(el * 2);
});

function 直接在调用中定义,参数 el 的值通过 forEach method 来传入每个元素的数据。

下一章介绍的 object 中,要定义一个 method 就可以使用 inline function 来建立:

    function person(name, age, color) {
        this.name = name;
        this.age = age;
        this.favColor = color;
        this.changeName = function(name) {
            this.name = name;
        }
    }

具体的介绍参考:JavaScript 入门教程之五 -- Objects

apply and call method

通过 function 的 apply()call() method,可以将 method 应用于其他外部 object 上。

object 将在下一章节进行介绍:JavaScript 入门教程之五 -- Objects

下面的示例中,我们将 person object 的 fullName method 应用在 person1 object上,也就是使用了 person1 的数据来调用 fullName

var person = {
    fullName: function () {
        console.log(this.firstName + " " + this.lastName);
    }
}

var person1 = {
    firstName: "marco",
    lastName: "nie"
}
person.fullName.apply(person1);

//output:
//marco nie

以上示例中,我们使用了 person1 的 properties 作为 person 的数据来调用 function。

如果调用的 function 需要传入参数,需要在 apply method 中以数组的形式传入参数:

var person = {
    fullName: function (city, country) {
        console.log(this.firstName + " " + this.lastName + ", " + city + " " + country);
    }
}

var person1 = {
    firstName: "marco",
    lastName: "nie"
}
person.fullName.apply(person1, ["hz", "china"]);

//output:
//marco nie, hz china

注意第一个参数还是一个 object,表示使用其 properties 作为被调用的 function 所在 object 的 properties,后面的数组作为 function 的参数数据。

call() method 和 apply() 功能类似,区别是传入 function 的参数时需要分开单独传入:

person.fullName.call(person1, "hz", "china");

以上指令效果和上面的示例一样。

使用这种方法可以实现调用 function 时将所有参数作为一个数组出入:

function test(a, b, c) {
    console.log(a + b + c);
}
var arr = [1, 2, 3];
test.apply(null, arr);

function 的常规使用方法就介绍完毕。

]]>
0 https://blog.niekun.net/archives/1992.html#comments https://blog.niekun.net/feed/archives/1992.html
JavaScript 入门教程之三 -- Conditionals and Loops https://blog.niekun.net/archives/1990.html https://blog.niekun.net/archives/1990.html Fri, 18 Dec 2020 11:10:00 +0800 admin The if Statement

在编程中,如果想要根据不同的情况执行不同的代码,可以使用 if conditional statements 状况指令来处理,语法如下:

if (condition) {
   statements
}

使用 if 语法来根据 condition 是否为 true 来确定是否执行 statements。下面举例说明,修改 html 文件 script 部分:

var a = 1;
if (a == 1) {
    alert("a is 1");
}

效果如下:
1.jpg

如果修改 condition 不为 true 则不会执行内部指令:

var a = 2;
if (a == 1) {
    alert("a is 1");
}

以上示例不会执行 alert function。

注意如果 statement 指令只有一句时,可以省略大括号,如下写法也是正确的:

var a = 1;
if (a == 1)
    alert("a is 1");

使用 else 申明来 condition 为 false 时执行的指令:

if (expression) {
   // executed if condition is true
}
else {
   // executed if condition is false
}

下面举例说明:

var a = 1;
var b = 2;
if (a < b)
    alert("a smaller than b");
else
    alert("a is bigger than b");

以上示例实现的动作也可以使用我们上一章提到的状况判断符来实现:

a < b ? alert("a smaller than b") : alert("a is bigger than b");

如果 condition 不止有两种情况要处理,可以使用 else if 申明多个情况下的指令,修改上面的示例:

var a = 2;
var b = 2;
if (a < b)
    alert("a smaller than b");
else if (a == b)
    alert("a equal to b");
else
    alert("a is bigger than b");

则会执行 alert("a equal to b"); 指令。

注意 else 在整个块中必须作为最后一个申明写在 ifelse if 之后,如果所有 condition 的条件都不满足则会执行 else 中的申明指令。

可以根据情况使用任意多个 else if 申明。

switch

当 condition 有多个情况需要处理时,使用 else if 就会很麻烦。这时候可以使用 switch statement 来处理,语法如下:

switch (expression) {
  case n1: 
     statements
     break;
  case n2: 
     statements
     break;
  default: 
     statements
}

switch expression 会被评估一次,然后在 case 中寻找匹配的结果并执行对应块的指令。

以下是一个简单示例:

var day = 2;
switch (day) {
    case 1:
        document.write("today is monday");
        break;
    case 2:
        document.write("today is tuesday");
        break;
    default:
        document.write("today is another day");
        break;
}

当 day 的值为 2,则会匹配到 case 2 并执行其内的指令。

当 JavaScript 执行到 break 后会跳出 switch 块,如果不写 break 则会顺序执行下面的指令,即使不满足其他的 case 条件。通常情况下每个 case 块结尾都应该有 break

default 关键词用来处理没有匹配到任何 case 情况,如果没有匹配到则执行 default 中的指令。如果不需要处理没有指定的 case 匹配到的情况时 default 可以被省略。

for 循环

使用 loop 循环可以多次执行一段代码,通过一些条件来控制循环次数及参数值。

JavaScript 提供三种类型的循环:for, while, 和 do while。

for 循环语法如下:

for (statement 1; statement 2; statement 3) {
   code block to be executed
}
  • Statement 1 在循环前被执行
  • Statement 2 定义执行循环的状态
  • Statement 3 在每次循环后被执行

下面的示例将输出 0 - 4:

for (var i=0; i<5; i++) {
    document.write(i + "<br>");
    
}

过程为:首先给变量 i 赋值为 1,执行循环的条件是 i<5,每次循环后给 i 加 1。当 i==4 时,执行完本次循环后 i 加 1 后值为 5,不满足第二项条件就会跳出循环。

statement 1 是可选项可以不写,如:

var i = 0;
for (; i<5; i++) {
    document.write(i + "<br>");
    
}

和第一个示例效果是一样的。

statement 1 也可以同时定义多个参数,使用逗号, 来隔离,如:

for (var i=0, j=2; i<5; i++) {
    document.write(i + "<br>");
    
}

如果 statement 2 返回 true 则会执行循环块内容,如果返回 false 则会结束循环。

同样的 statement 2 也是可选项,也可以不定义,但是必须在循环块内定义 break 不然就会无线循环下去:

for (var i=0; ; i++) {
    if (i >= 5)
        break;
    document.write(i + "<br>");
}

statement 3 用来修改初始变量的值,同样也是可选项,可以在循环块内部直接修改初值:

for (var i=0; i<5 ;) {
    document.write(i + "<br>");
    i++;
}

while 循环

while 循环也是用来实现当某个 condition 为 true 时循环一个代码块。语法如下:

while (condition) {    
   code block
}

下面的示例将输出 0 - 10 的数字:

var i = 0;
while (i<=10) {
    document.write(i + "<br>");
    i++;
}

当 condition 一直为 true 时,循环就会继续下去。我们应该避免无限循环情况出现。

do while 循环

do while 循环是 while 循环的一种变形,它会首先执行一次循环块然后判断 condition 条件。语法如下:

do {
   code block
}
while (condition);

注意 while 结尾的分号; 不能省略。

下面的示例会输出 0 - 4:

var i = 0;
do {
    document.write(i + "<br>");
    i++;
} while (i<5);

循环块至少会执行一次,即使 condition 为 false。

break

break 指令用来跳出循环继续执行后续程序。

下面示例中,将只会输出 0 - 4 的数字:

for (var i=0; i<10 ; i++) {
    if (i == 5)
        break;
    document.write(i + "<br>");
}

在 function 中可以使用 return 来跳出当前代码块,下一章节将会介绍。

continue

continue 指令用来仅跳出当次循环,然后继续进行下一次循环。

下面示例将会输出 0- 10,但不包括 5:

for (var i=0; i<10 ; i++) {
    if (i == 5)
        continue;
    document.write(i + "<br>");
}

当 i 等于 5 时会跳出本次循环所以不会输出 5,但是会继续进行下面的循环。

]]>
0 https://blog.niekun.net/archives/1990.html#comments https://blog.niekun.net/feed/archives/1990.html
JavaScript 入门教程之二 -- 基本概念 https://blog.niekun.net/archives/1985.html https://blog.niekun.net/archives/1985.html Thu, 17 Dec 2020 16:33:00 +0800 admin 数学运算符

有以下运算符可以使用:
1.png

加减运算使用方法很简单:

var b = 10 + 2;
var c = b - 3;
document.write(c);

可以使用 eval() 来将计算结果转换为字符串形式:

var d = eval("10 + 2 + 1")
document.write(d);

d 的结果就是 "13"

乘法使用星号* 来表示,以下三种写法都是正确的:

var e = 2 * 3;
var f = 2 * '3';
var g = '2' * '3';
document.write(g);

注意如果尝试对一个字符串进行乘法计算会返回:NaN (Not a Number):

var h = 'hello' * 2;
document.write(h);

//output:
//NaN

除法使用斜线/ 表示

var i = 1 / 2;

注意被除数不能为 0。

取模运算使用% 表示:

var j = 10 % 3;
document.write(j);

//output:
//1

可以对整数和浮点型数字进行取模运算。

递增和递减

2.png

使用++ 表示给对应变量进行加 1 运算,操作符写在变量前则返回加 1 后的值。写在后边返回加 1 前的值。
使用-- 表示给对应变量进行减 1 运算,操作符写在变量前则返回减 1 后的值。写在后边返回减 1 前的值

var k = 10;
var l = k++;
var m = ++l;
document.write(m);

//output:
//11

分配符 Assignment Operators

可用的分配符有:
3.png

除了常规用法外,也可以在一行命令中使用多个分配符,例如:

var n = 4;
var o = 5;
o += n += 3;
document.write(o);

//output:
//12

比较符 Comparison Operators

在逻辑判断中使用比较符来比较数据是否有区别。结果为 true 或 false。

例如可以使用 equal to (==) operator 来比较两个数据是否一样:

var p = 2;
document.write(p == 1);

//output:
//false

所有的数据类型都可以进行比较,返回值只有 true 和 false。但需要注意比较的两个数据需要是同一类型的。

以下是可用的比较符:
4.png

逻辑判断符 Logical Operators

逻辑判断符用来评估一个表达式,返回结果为 true 和 false。可用的逻辑判断符为:AND, OR, NOT

  • && - AND 如果判断块都为 true,则返回 true
  • || - OR 如果任何一个判断块为 true,则返回 true
  • ! - NOT 如果判断块为 false,则返回 true

下面的示例是对两个判断块进行 与AND 操作:

(4 > 2) && (1 < 3);

以上两个判断块都为 true 则整个逻辑判断结果为 true。

状况判断符 Conditional (Ternary) Operator

状况判断符用来根据一些实际状态给某个变量赋值。语法如下:

variable = (condition) ? value1: value2 

下面举例说明:

var age = 20;
var isAdult = (age < 18) ? "too young" : "old enough";
document.write(isAdult);

//OUTPUT:
//old enough

以上示例中,如果 age 小于 18 则 isAdult 赋值为 "too young",如果 age 大于等于 18 则 isAdult 赋值为 "old enough"。

字符串操作符 String Operators

我们可以使用级联符+ 来将多个字符串组合成一个新的字符串。例如:

var str1 = "this is a ";
var str2 = "test string";
document.write(str1 + str2);

//output:
// this is a test string

注意一个数字使用引号括起来后就成了一个字符串,如:"42" 表示一个字符串。。

]]>
0 https://blog.niekun.net/archives/1985.html#comments https://blog.niekun.net/feed/archives/1985.html
JavaScript 入门教程之一 -- 总览 https://blog.niekun.net/archives/1979.html https://blog.niekun.net/archives/1979.html Thu, 17 Dec 2020 10:31:00 +0800 admin JavaScript(通常缩写为JS)是一种高级的、解释型的编程语言。JavaScript是一门基于原型、函数先行的语言,是一门多范式的语言,它支持面向对象程序设计,命令式编程,以及函数式编程。最初命名为 Mocha,1995年9月在 Netscape Navigator 2.0 的 Beta 版中改名为 LiveScript,同年12月,Netscape Navigator 2.0 Beta 3 中部署时被重命名为 JavaScript,当时网景公司与昇阳电脑公司组成的开发联盟为了让这门语言搭上 Java 这个编程语言“热词”,因此将其临时改名为 JavaScript,日后这成为大众对这门语言有诸多误解的原因之一。

不同于服务器端脚本语言,例如 PHP 与 ASP,JavaScript 主要被作为客户端脚本语言在用户的浏览器上运行,不需要服务器的支持。所以在早期程序员比较青睐于 JavaScript 以减少对服务器的负担,而与此同时也带来另一个问题:安全性。而随着服务器变得强大,现在的程序员更喜欢运行于服务端的脚本以保证安全,但 JavaScript 仍然以其跨平台、容易上手等优势大行其道。同时,有些特殊功能(如AJAX)必须依赖 JavaScript 在客户端进行支持。随着引擎如 V8 和框架如 Node.js 的发展,及其事件驱动及异步 IO 等特性,JavaScript 逐渐被用来编写服务器端程序。且在近几年中,Node.js 的出世,让 JavaScript 也具有了一定的服务器功能。

我们的第一个 JavaScript

我们首先来了解一些基本概念,给 web 网页加上 JavaScript。

在 web 上,JavaScript 位于 HTML document 内,可以在任意位置。js 内容使用 <script></script> 包围起来:

<script>
   ...
</script>

让我们使用 JavaScript 在网页上打印出一个 Hello World,新建一个 html 文件,内容如下:

<!DOCTYPE html>
<html>
    <head>
        <title>Page Title</title>
    </head>
    <body>
        
    </body>
    <script>
        document.write("hello world");
    </script>
</html>

使用 document.write() function 用来给 html document 写入内容。注意此 function 只用来测试,实际使用中会覆盖 document 中已有的内容。

JavaScript 每条指令结尾使用分号; 来标记。

文件保存后可以使用浏览器打开查看效果。

js 中还支持标准 html markup 标记语法,我们给 hello world 设置属性:

    <script>
        document.write("<h1>hello world</h1>");
    </script>

这时候文字会以标题模式显示。

也可以在浏览器终端 console 中输出信息,使用 console.log() function 实现:

    <script>
        document.write("<h1>hello world</h1>");
        console.log("this is a test log");
    </script>

打开浏览器的调试窗口即可看到输出信息,chrome 浏览器使用快捷键 ctrl+shift+I 或设置中打开调试:
1.jpg

点击 console 栏查看:
2.jpg

注释

对于不需要被执行的语句可以使用符号来忽略,使用双斜杠 // 注释单行内容,使用 /**/ 注释一个块。

修改我们上面的 html 文件的 js 块:

    <script>
        // this is a commit
        document.write("<h1>hello world</h1>");
        console.log("this is a test log");
        /* this code create
            a alert box
        */
        alert("this is a alert box");
    </script>

alert() function 用来弹出一个提示窗口。

变量

variables 变量是存储数据的容器。变量的值可以再程序中被修改。

使用关键词 var 申明一个变量:

var x = 10;

以上指令给变量 x 分配数据 10。注意我们在此使用 assign 分配来描述这个过程,因为在 JavaScript 中等于号= 会调用 assignment operator 操作符,而不是 equal to operator 操作符。

变量名是大小写敏感的,也就是说 nameName 是两个变量。

让我们输出一个变量值到浏览器:

var x = 10;
document.write(x);

变量定义的基本原则:

  • 首字符必须是这三者之一:字母,下划线_$ 符。后续字符可以是字母、数字,下划线或者 &
  • 名称不能包含数学运算符或操作符
  • 不能包含空格
  • 不能使用特殊字符,如:# & % 等
  • 不能使用连字符-,这是减法保留符

数据类型

JavaScript 支持多种类型的数据:numbers, strings, arrays 等。

number

number 可以是整数或小数:

var a = 10;
var b = 1.1;

变量分配的数据类型可以任意修改,例如重新分配 a 的数据为 string 字符串:

a = "this is a strings";

string

使用 string 可以来存储及操作文本信息。使用引号来包裹内容,单引号' 或双引号" 都可以:

var a = 'marco';
var b = "john";

如果要在 string 内部使用也引号,可以通过内外使用不同的单双引号来实现,js 会自动区分:

var a = "this is a 'test'";

如果内外想要使用同一个引号形式可以在内部使用转义符 \ 来实现:

a = "this \"is\" a \"other\" test";

转义符可以将特殊字符作为普通字符使用,也可以实现特殊功能,一些列表是常用的转码:
3.png

注意字符串的包裹分号需要前后统一,起始用了单引号结尾也必须使用单引号,否则会报错。

Boolean

Boolean 只有两种结果:true 和 false。

如果你需要一种只有两种可能的结果的数据类型,就可以使用 Boolean:

var isActive = true;

注意 Boolean 类型的值如果是:0 (zero), null, undefined, empty string 则都是 false,其他如果有一个真实数据的都为 true。

]]>
0 https://blog.niekun.net/archives/1979.html#comments https://blog.niekun.net/feed/archives/1979.html