第三十四讲——JavaScript

第三十四讲——JavaScript

在解释的时候会自动给没有分号的行加分号 ,要注意

1.—— 快速入门

基本语法

Decoration

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <!-- script 标签写 javaScript代码-->

    <!--第一种导入 内部导入方法-->
    <script>
        alert("hello World!")
    </script>

    <!-- 第二种导入 外部引入 script 链接-->
    <script src="js/1.%20第一个javaScript.js"></script>

    <!--默认type 自己不用加-->
    <script type="javascript"></script>
</head>
<body>


<!-- 这里也可以存放-->
</body>
</html>

Result



2——数据类型

数值、文本、图形、音频视频、、、

变量

不能以数字和 # 开头

 var 王者荣耀 = "青铜";

number

js 不区分小数和整数, number

123 // 整数
123.1 // 浮点数
1.12e3 // 科学计数法
-99
Nan   // not a  number : 不是一个数字

字符串

‘abc’ “abc”

如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I'm,空格,OK这6个字符。

如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:

'I\'m \"OK\"!';

表示的字符串内容是:I'm "OK"!

字符串的拼接可以用 +


  1. 正常字符串我们使用,单引号,或者双引号包裹
  2. 注意转义字符 \
\'
\n
\t
\u4e2d   \u####  Unicode字符
\x41    Ascll字符

  1. 多行字符串的编写
 <script>
'use strict'
/*支持多行字符串的编写  tab键 上面 esc键 下面  ``   */
console.log(`
周日
星期几
卷王之王
`);
 </script>


  1. 模板字符串

要把多个字符串连接起来,可以用+号连接:

var name = '小明';
var age = 20;
var message = '你好, ' + name + ', 你今年' + age + '岁了!';
alert(message);

如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:

var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);

  1. 字符串长度
let student = "student";
student.length
  1. 操作字符串

要获取字符串某个指定位置的字符,使用类似Array的下标操作,索引号从0开始:

var s = 'Hello, world!';

s[0]; // 'H'
s[6]; // ' '
s[7]; // 'w'
s[12]; // '!'
s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined

需要特别注意的是,字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:

var s = 'Test';
s[0] = 'X';
alert(s); // s仍然为'Test'

JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串:

toUpperCase

toUpperCase()把一个字符串全部变为大写:

var s = 'Hello';
s.toUpperCase(); // 返回'HELLO'

toLowerCase

toLowerCase()把一个字符串全部变为小写:

var s = 'Hello';
var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower
lower; // 'hello'

运算

布尔值

true false

逻辑运算

&&		两个为真,结果为真

|| 		一个为真,结果为真

!      真即假    假即真

比较运算符

=

==   	相对等于 (类型不一样、值一样,也会判断true)

===  	绝对等于(类型和值 必须一致)

坚持使用 === 用来对比是否相等

注意:

  • NaN==NaN 这个与所有的数值都不相等,包括它自己
  • 只能通过 isNaN(NaN)来判断这个数是否是NaN

浮点数问题

console.log(1/3) === (1-2/3)

尽量避免使用浮点数,进行运算存在精度问题

Math.abs((1/3)- (1-2/3))<0.0000000000000001


null 和 undefined

  • null 空
  • undefined 未定义

数组

js 数组允许不同类型,为一个数组

 /* 保证代码的可读性, 数组使用[]*/
var arr = [1,2,3,"hello",null,true];

new Array(1,2,3,"hello",null,true);

arr.length() // 长度
// 注意: 加入给 arr.length 赋值,数组大小就会发生变化~,如果赋值过小,元素就会丢失

arr.indeOf(2) // 通过元素获得下标索引 在字符串中寻找子串  字符串的 "1" 和数字 1 是不同的

arr.sort() // 排序

let arr = [1,2,3,4,5,6];
arr.reverse()  // 数组反转
(6) [6, 5, 4, 3, 2, 1]

如果不给slice()传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以很容易地复制一个Array

// slice 截取 Array 的一部分,返回一个新数组,类似于 subString
// 
let arr = [1,2,3,4,5,6];
arr.slice(2)
(4) [3, 4, 5, 6]
arr.slice(2,5)
(3) [3, 4, 5]

splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:

var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']

puth(); // 可以添加多个元素到尾部
pop; //   只能弹出尾部一个元素

unshift(); // 可以添加多个元素到头部
shift; //   只能弹出头部一个元素

let arr = [1,2,3,4,5,6];
let num = [6,6,6];
arr.concat(num);
(9) [1, 2, 3, 4, 5, 6, 6, 6, 6]
// concat() 并不会修改数组,只是会返回一个新的数组

let arr = [1,2,3,4,5,6];
arr.join("_")
'1_2_3_4_5_6'  // 连接符 join 

let arr = [[1,6],[0,6],[2,6,'$']];
arr[2][1]  // 二维数组
6

Map和Set

ES6 新特性~

Map;

      'use strict'

      // 学生的成绩 学生的名字

       /*var names = ["tom","jack","long"];
        var score = [100,90,85];*/

        var map = new Map([['tom',100],['jack',90],['long',85]]);
        var name = map.get("tom"); // 通过key 或得 value
        console.log(name);

        map.set('admin',1245); // 新增 修改
		map.delete('tom');
        console.log(map);

Set; 无序不重复的集合

var set = new Set([3,1,1,1,1]) // Set 可以去重
console.log(set); // 3  1

set.add(2);
set.delete(1);
console.log(set.has(3)); // 是否包含某元素

iterator

ES6 新特性

遍历 map

var map = new Map([["tom",100],["jack",95],["long",85]]);
for (let x of map){
    console.log(x);
}

遍历 set

var set = new Set([5,6,7]);
for (let x of set){
    console.log(x);
}

遍历数组

var arr = [3,4,5];
for (let  x of arr){
    console.log(x);
}

对象

访问属性是通过.操作符完成的,但这要求属性名必须是一个有效的变量名。如果属性名包含特殊字符,就必须用''括起来:

var xiaohong = {
    name: '小红',
    'middle-school': 'No.1 Middle School'
};

xiaohong的属性名middle-school不是一个有效的变量,就需要用''括起来。访问这个属性也无法使用.操作符,必须用['xxx']来访问:

xiaohong['middle-school']; // 'No.1 Middle School'
xiaohong['name']; // '小红'
xiaohong.name; // '小红'

对象用大括号,数组用中括号

Description

<script>
    //  对象里的 内容是键值对,每个属性用 逗号隔开!,最后一个不用符号结束!
    // 定义了一个 person 属性 他有多个属性
    var person = {
        'use strict'
        // 属性名 : 属性值
        name : "XXZ",
        age: 3,
        tags:[ 'js','java','web','...']
        
         name : "XXZ",
         age: 18,
         email : "2991758426@qq.com",
         score : 28,

    }
</script>

Console

// 调用的方法 在控制台直接调用
person.name
'XXZ'

JS 中所有的键都是字符串,值是任意对象

person
{name: 'XXZ', score: 28, pp: 'hhhhh'}
person["name"]
'XXZ'

  1. 使用一个不存在的对象属性,不会报错, undefind
person.pp
undefined

  1. 动态删减属性,通过 delete 删除对象的属性
delete person.age
true
person.age
undefined
  1. 动态的添加属性, 直接给新的属性添加值即可
person.pp = "hhhhh"
'hhhhh'
person.pp
'hhhhh'

  1. 判断属性值是否在这个对象中 xxx in xxx
person
{name: 'XXZ', score: 28, pp: 'hhhhh'}

age in person // 键是字符串 要用 “” 包裹
VM790:1 Uncaught ReferenceError: age is not defined
    at <anonymous>:1:1

"age" in person
false

"toString" in person // 和继承有关,
true

  1. 判断一个属性是否是这个对象自身拥有的 hasOwnProperty()
person.hasOwnProperty("toString")
false
person.hasOwnProperty("name")
true

流程控制


if 判断

        'use strict'
    var age = 3;
    if (age>3){         // 第一个条件
        alert("hahahahahaha");
    }  else if( age===8){     // 第二个条件  记得要写绝对等于 三个===
        alert("kuwa~")
    } else{                 // 第三个条件
        alert(" JavaScript  ")
    }
    

如果if的条件判断语句结果不是truefalse怎么办?例如:

var s = '123';
if (s.length) { // 条件计算结果为3
    //
}

JavaScript把nullundefined0NaN和空字符串''视为false,其他值一概视为true,因此上述代码条件判断的结果是true


while

        'use strict'
		var age = 3;
		while(age<100){
			age+=1;
			console.log(age)
		}
	
---
forEach

       		'use strict'
    		var age = [12,564,818,6,49,16,8];
    		// 函数
        	age.forEach( function (value) {
            console.log(value)
        })
    
---

for循环的一个变体是for ... in循环,它可以把一个对象的所有属性依次循环出来:

var o = {
    name: 'Jack',
    age: 20,
    city: 'Beijing'
};
for (var key in o) {
    console.log(key); // 'name', 'age', 'city'
}	

要过滤掉对象继承的属性,用hasOwnProperty()来实现:

var o = {
    name: 'Jack',
    age: 20,
    city: 'Beijing'
};
for (var key in o) {
    if (o.hasOwnProperty(key)) {
        console.log(key); // 'name', 'age', 'city'
    }
}

由于Array也是对象,而它的每个元素的索引被视为对象的属性,因此,for ... in循环可以直接循环出Array的索引:

var a = ['A', 'B', 'C'];
for (var i in a) { // 每个元素的索引被视为对象的属性 例如 ; 0,‘A’; 0就是属性
    console.log(i); // '0', '1', '2'
    console.log(a[i]); // 'A', 'B', 'C'
}
//for/in 语句用于循环对象属性。

请注意!!!,for ... inArray的循环得到的是String而不是Number


do ... while

最后一种循环是do { ... } while()循环,它和while循环的唯一区别在于,不是在每次循环开始的时候判断条件,而是在每次循环完成的时候判断条件:

var n = 0;
do {
    n = n + 1;
} while (n < 100);
n; // 100

do { ... } while()循环要小心,循环体会至少执行1次,而forwhile循环则可能一次都不执行。


严格检查模式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--
    前提: IDEA 需要设置支持 ES6 语法
    'use strict';   严格检查模式,预防 JAVAScript 的随意性导致产生的一些问题
    如果书写不规范会报警
    必须写在 js 的第一行,不然效力不大
    在严格检查模式中,不推荐使用 var 局部变量建议都使用let 去定义~

    var 全局变量,当整合的时候会有冲突
    -->
    <script>
        'use strict'

           a=1;
        /*a 会警告 变红*/

         let i = 1;
        // ES6  Let
    </script>
</head>
<body>

</body>
</html>

Result


3——函数

定义函数

在JavaScript中,定义函数的方式如下:

function abs(x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

上述abs()函数的定义如下:

  • function指出这是一个函数定义;
  • abs是函数的名称;
  • (x)括号内列出函数的参数,多个参数以,分隔;
  • { ... }之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。

请注意,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。

如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined

由于JavaScript的函数也是一个对象,上述定义的abs()函数实际上是一个函数对象,而函数名abs可以视为指向该函数的变量。

因此,第二种定义函数的方式如下:

var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};

在这种方式下,function (x) { ... }是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,所以,通过变量abs就可以调用该函数。

上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。


调用函数

调用函数时,按顺序传入参数即可:

abs(10); // 返回10
abs(-9); // 返回9

由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:

abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9

传入的参数比定义的少也没有问题:

abs(); // 返回NaN

此时abs(x)函数的参数x将收到undefined,计算结果为NaN

要避免收到undefined,可以对参数进行检查:

function abs(x) {
    if (typeof x !== 'number') {
        // 手动抛出异常来判断
        throw 'Not a number';
    }
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

arguments

JavaScript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array

'use strict'
function foo(x) {
    console.log('x = ' + x); // 10
    for (var i=0; i<arguments.length; i++) {
        console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
    }
}
foo(10, 20, 30);

Result

x = 10
arg 0 = 10
arg 1 = 20
arg 2 = 30

利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值:

function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x >= 0 ? x : -x;
}

abs(); // 0
abs(10); // 10
abs(-9); // 9

实际上arguments最常用于判断传入参数的个数。你可能会看到这样的写法:

// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
function foo(a, b, c) {
    if (arguments.length === 2) {
        // 实际拿到的参数是a和b,c为undefined
        c = b; // 把b赋给c
        b = null; // b变为默认值
    }
    // ...
}

要把中间的参数b变为“可选”参数,就只能通过arguments判断,然后重新调整参数并赋值。

rest参数

由于JavaScript函数允许接收任意个参数,于是我们就不得不用arguments来获取所有参数:

function foo(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

为了获取除了已定义参数ab之外的参数,我们不得不用arguments,并且循环要从索引2开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的rest参数,有没有更好的方法?

ES6标准引入了rest参数,上面的函数可以改写为:

function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []

rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定ab,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

因为rest参数是ES6新标准,所以你需要测试一下浏览器是否支持。请用rest参数编写一个sum()函数,接收任意个参数并返回它们的和:

'use strict';
// 测试:
var i, args = [];
for (i=1; i<=100; i++) {
    args.push(i);
}
if (sum() !== 0) {
    console.log('测试失败: sum() = ' + sum());
} else if (sum(1) !== 1) {
    console.log('测试失败: sum(1) = ' + sum(1));
} else if (sum(2, 3) !== 5) {
    console.log('测试失败: sum(2, 3) = ' + sum(2, 3));
} else if (sum.apply(null, args) !== 5050) {
    console.log('测试失败: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
} else {
    console.log('测试通过!');
}

Run

小心你的return语句

前面我们讲到了JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑:

function foo() {
    return { name: 'foo' };
}

foo(); // { name: 'foo' }

如果把return语句拆成两行:

function foo() {
    return
        { name: 'foo' };
}

foo(); // undefined

要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

function foo() {
    return; // 自动添加了分号,相当于return undefined;
        { name: 'foo' }; // 这行语句已经没法执行到了
}

所以正确的多行写法是:

function foo() {
    return { // 这里不会自动加分号,因为{表示语句尚未结束
        name: 'foo'
    };
}

变量作用域

在JavaScript中,用var申明的变量实际上是有作用域的。

如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:

'use strict';

function foo() {
    var x = 1;
    x = x + 1;
}

x = x + 2; // ReferenceError! 无法在函数体外引用变量x

如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:

'use strict';

function foo() {
    var x = 1;
    x = x + 1;
}

function bar() {
    var x = 'A';
    x = x + 'B';
}

由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:

'use strict';

function foo() {
    var x = 1;
    function bar() {
        var y = x + 1; // bar可以访问foo的变量x!
    }
    var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}

如果内部函数和外部函数的变量名重名怎么办?来测试一下:

'use strict';

function foo() {
    var x = 1;
    function bar() {
        var x = 'A';
        console.log('x in bar() = ' + x); // 'A'
    }
    console.log('x in foo() = ' + x); // 1
    bar();
}

foo();

Result

 Run
x in foo() = 1
x in bar() = A

这说明JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

变量提升

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:

'use strict';

function foo() {
    var x = 'Hello, ' + y;
    console.log(x);
    var y = 'Bob';
}

foo();

虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是console.log显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码相当于:

function foo() {
    var y; // 提升变量y的申明,此时y为undefined
    var x = 'Hello, ' + y;
    console.log(x);
    y = 'Bob';
}

由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量:

function foo() {
    var
        x = 1, // x初始化为1
        y = x + 1, // y初始化为2
        z, i; // z和i为undefined
    // 其他语句:
    for (i=0; i<100; i++) {
        ...
    }
}

全局作用域

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'

因此,直接访问全局变量course和访问window.course是完全一样的。

你可能猜到了,由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:

'use strict';

function foo() {
    alert('foo');
}

foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用

进一步大胆地猜测,我们每次直接调用的alert()函数其实也是window的一个变量:

'use strict';

window.alert('调用window.alert()');
// 把alert保存到另一个变量:
var old_alert = window.alert;
// 给alert赋一个新函数:
window.alert = function () {}

此时无法使用 alert 方法 alert('无法用alert()显示了!');

// 恢复alert:
window.alert = old_alert;
alert('又可以用alert()了!');

这说明JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。

名字空间

全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。

减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:

// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
    return 'foo';
};

把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。

许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。

局部作用域

由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的:

'use strict';

function foo() {
    for (var i=0; i<100; i++) {
        //
    }
    i += 100; // 仍然可以引用变量i
}

为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:

'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    // SyntaxError:
    i += 1;
}

常量

由于varlet申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:

var PI = 3.14;

ES6标准引入了新的关键字const来定义常量,constlet都具有块级作用域:

'use strict';

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14

解构赋值

从ES6开始,JavaScript引入了解构赋值,可以同时对一组变量进行赋值。

什么是解构赋值?我们先看看传统的做法,如何把一个数组的元素分别赋值给几个变量:

var array = ['hello', 'JavaScript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];

现在,在ES6中,可以使用解构赋值,直接对多个变量同时赋值:

'use strict';

// 如果浏览器支持解构赋值就不会报错:
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
// x, y, z分别被赋值为数组对应元素:
console.log('x = ' + x + ', y = ' + y + ', z = ' + z);

Result

x = hello, y = JavaScript, z = ES6

注意,对数组元素进行解构赋值时,多个变量要用[...]括起来。

如果数组本身还有嵌套,也可以通过下面的形式进行解构赋值,注意嵌套层次和位置要保持一致:

let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'

解构赋值还可以忽略某些元素:

let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
z; // 'ES6'

如果需要从一个对象中取出若干属性,也可以使用解构赋值,便于快速获取对象的指定属性:

'use strict';

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};
var {name, age, passport} = person;
// name, age, passport分别被赋值为对应属性:
console.log('name = ' + name + ', age = ' + age + ', passport = ' + passport);

Result

name = 小明, age = 20, passport = G-12345678

对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school',
    address: {
        city: 'Beijing',
        street: 'No.1 Road',
        zipcode: '100001'
    }
};
var {name, address: {city, zip}} = person;
name; // '小明'
city; // 'Beijing'
zip; // undefined, 因为属性名是zipcode而不是zip
// 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
address; // Uncaught ReferenceError: address is not defined

使用解构赋值对对象属性进行赋值时,如果对应的属性不存在,变量将被赋值为undefined,这和引用一个不存在的属性获得undefined是一致的。如果要使用的变量名和属性名不一致,可以用下面的语法获取:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport属性赋值给变量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是变量,而是为了让变量id获得passport属性:
passport; // Uncaught ReferenceError: passport is not defined

解构赋值还可以使用默认值,这样就避免了不存在的属性返回undefined的问题:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};

// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
name; // '小明'
single; // true

有些时候,如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误:

// 声明变量:
var x, y;
// 解构赋值:
{x, y} = { name: '小明', x: 100, y: 200};
// 语法错误: Uncaught SyntaxError: Unexpected token =

这是因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。解决方法是用小括号括起来:

({x, y} = { name: '小明', x: 100, y: 200});

使用场景

解构赋值在很多时候可以大大简化代码。例如,交换两个变量xy的值,可以这么写,不再需要临时变量:

var x=1, y=2;
[x, y] = [y, x]

快速获取当前页面的域名和路径:

var {hostname:domain, pathname:path} = location;

如果一个函数接收一个对象作为参数,那么,可以使用解构直接把对象的属性绑定到变量中。例如,下面的函数可以快速创建一个Date对象:

function buildDate({year, month, day, hour=0, minute=0, second=0}) {
    return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}

它的方便之处在于传入的对象只需要yearmonthday这三个属性:

buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)

也可以传入hourminutesecond属性:

buildDate({ year: 2017, month: 1, day: 1, hour: 20, minute: 15 });
// Sun Jan 01 2017 20:15:00 GMT+0800 (CST)

使用解构赋值可以减少代码量,但是,需要在支持ES6解构赋值特性的现代浏览器中才能正常运行。目前支持解构赋值的浏览器包括Chrome,Firefox,Edge等。


方法 与指向apply

定义方法

方法就是把函数放在对象里面,对象只有两个东西: 属性和方法

Desrcipt

    var xxz = {
        name : '项晓忠',
        birthday: 1999,
        // 方法
        age : function (){
            // 今年 - 出生年
            var  now = new Date().getFullYear();

            return now-this.birthday;
        }

    }
// 调用方法一定要用括号
xxz.age()
23

分析 this

function   getAge(){
    // 今年 - 出生年
     var now = new Date().getFullYear();
     return now -this.birthday;

}

var xxz = {
    name : '项晓忠',
    birthday: 1999,
    // 方法 一
    age : function (){
        // 今年 - 出生年
        var  now = new Date().getFullYear();

        return now-this.birthday;
    },

}

// xxz.age()  OK
//  方法二 console  直接调用  getAge() NaN   因为this指向window  默认指向调用对象  其实这里和java 是一样的

apply : 所有的函数都有 apply 属性

// console 输入 函数.apply 调用就 ok  apply 格式 :  指向对象 , 参数
getAge.apply(xxz,[]);  
23

4——内部对象

typeof 123
'number'
typeof '123'
'string'
typeof true
'boolean'
typeof NaN
'number'
typeof []
'object'
typeof {}
'object'
typeof Math.abs
'function'
typeof undefined
'undefined'

Date

var now = new  Date(); // 'Wed Jan 19 2022 14:02:49 GMT+0800 (中国标准时间)'

now.getFullYear(); // 年
now.getMonth(); // 月 0~11
now.getDate(); // 日
now.getDay(); // 星期几
now.getHours() ;// 时
now.getMinutes(); // 分
now.getSeconds(); // 秒

now.getTime(); // 时间戳  世界统一 1970 1.1 0:00  毫秒数

console.log(new Date(1642572653078));  // 控制台打印出时间戳

now = new Date(1642572653078);
now.toLocaleString();
alert(now.toLocaleString()) // Wed Jan 19 2022 14:10:53 GMT+0800 (中国标准时间)
now.toGMTString();
// 'Wed, 19 Jan 2022 06:10:53 GMT'

JSON

  • JSON ( JavaScript Object Notation, JS 对象简谱 ) 是一种轻量级的数据交换格式。

  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

自己的理解 SJON 是一种转化的语言,用于对象和字符串之间的转换,从对象(stringify)到字符串,或从字符串(parse)到对象的转换

javaScript 中 一切皆为对象,任何 js 支持的类型都可以用 JSON 来表示

格式:

  • 对象都用 {}
  • 数组都用[]
  • 所有的键值对都是用 key: value
var user = {
    name : "xxz",
    age: 3,
    sex : "男",
};

// 对象转化成json 字符串 '{"name":"xxz","age":3,"sex":"男"}'  // 里面都用双引号 外面用单引号
var jsonUser = JSON.stringify(user);

// json 字符串转化为对象,参数为json 字符串
var obj = JSON.parse('{"name":"xxz","age":3,"sex":"男"}');

5——面对对象编程

  • 类是模板

  • 对象是一个具体的实例

原型对象继承

Description

'use strict';
var student = {
    name : "xxz",
    age: 3,
    sex : "男",
    run : function(){
        console.log(this.name+"\trun...");
    }
};

var bird ={
  fly : function(){
      console.log(this.name+"\tfly...");
  }
};

var xiaoming =  {
    name : "xm",
};

// 小明的原型(父类)是  bird
xiaoming.__proto__= bird;

xiaoming.__proto__= student; // 也可以是 student

class 继承 属性 方法

class student {

    constructor(name){
        this.name = name;
    }

    hello(){
        alert("hello");
    }

}

var xiaoming =  new student("xiaoming");
var xiaohon =  new student("xiaohon");

Description

class student {

    constructor(name){
        this.name = name;
    }

    hello(){
        alert("hello");
    }

}

class xiaoStudent extends student{
    constructor(name,grade) {
        super();
        this.grade = grade;
    }

    myGrade(){
        alert("我是一名小学生");
    }

}




var xiaoming =  new student("xiaoming");
var xiaohon =  new xiaoStudent("xiaohon",2);

6.——操作BOM对象

BOM : (Browser,Object,module)浏览器对象模型

这些是浏览器的内核

  • IE 6~11
  • Chrome (谷歌浏览器)
  • Safari (ipone 浏览器)
  • FireFox (linux 自带)

window : 表示浏览器的窗口

window.alert('yujunjun');
undefined
window.innerHeight;  // 内部表示浏览器内容显示的大小  随着console 的大小变化而变化
190
window.innerWidth;
927
window.outerHeight  //外部表示整个窗口大小
1407
window.outerWidth
1406

Navigator :封装了浏览器的信息

  • 大多数情况不会使用‘navigator’ 对象,因为会被认为修改
  • 不建议使用这些属性来判断和编写代码
// 可以检测 该浏览器是否可以支持加载某程序

navigator.appName// 获取当前程序的名字
'Netscape'
navigator.appVersion
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.62'
navigator.userAgent // 客户端信息
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.62'
navigator.platform
'Win32'

screen : 屏幕

screen.width
2560
screen.height
1440

location (地址)

location 表示当前页面的 URL 信息

location.host
'www.baidu.com'

location.reload()  // 刷新网页

location.href
'https://www.baidu.com/'

location.assign("http://www.4399.com/");  // 设置新的地址
undefined
已导航到http://www.4399.com/

Document : 代表当前的页面

<dl id="app" >
    <dt  >JAVA</dt>
    <dl>javaSE</dl>
    <dl>javaEE</dl>
</dl>

<script>
    let byId = document.getElementById("app"); // 可以byid 就可以by class by 标签
    console.log(byId);
    
    // 还可以获得 cookie  控制台输入 document.cookie;
    
    
</script>


history

history.back(); // 返回
history.forward(); // 前进

7.—— 操作DOM对象

Document Object Module 文档对象模型

浏览器网页就是一个 Dom 树形结构

  • 得到节点
  • 更新: 更新Dom 节点
  • 删除: 删除一个 Dom 节点
  • 添加: 添加一个新的节点 追加 插入

要操作一个 Dom 节点,就必须

获得 Dom 节点

<div id="father" >
    <h1>标题 1 </h1>
    <p id="p1">p1</p>
    <p class="p2">p2</p>
</div>

<script>

    let h1 = document.getElementsByTagName('h1');// 通过标签选择节点
    let p1 = document.getElementById('p1');  // 通过id 选择节点
    let p2 = document.getElementsByClassName('p2'); // 通过类选择节点

    let father = document.getElementById("father");

    var children = father.children; // 获取父节点下所有子节点
    // father.firstChild   第一个节点
    // father.lastChild    最后一个节点
    

</script>

这是原生代码 , 之后会用 jQuery

更新节点

<div id="id1"></div>

<script>
    let id1 = document.getElementById('id1');
</script>

操作文本

  • id1.innerText='123' 修改文本的值

  • id1.innerHTML = '<strong>456</strong>' //  能解析 html 代码
    
    

操作js

id1.style.color = 'red'  // 属性用字符串包裹

id1.style.fontSize = '20px'; // 驼峰命名属性问题

id1.style.padding = '2em'

删除节点

删除节点的步骤: 先获取父节点,再通过父节点删除自己

<div id="father" >
    <h1>标题 1 </h1>
    <p id="p1">p1</p>
    <p class="p2">p2</p>
</div>

<script>

    let self = document.getElementById("p1");

    let father = self.parentElement;

    father.removeChild(self); // p1 就移除了

    father.children;// 获取所有孩子

    // 删除是一个动态的过程
    father.removeChild(father.children[0]);
    father.removeChild(father.children[1]);
    // 注意: 删除多个节点的时候,children 是时刻变化的,删除节点的时候一定要注意
    
</script>

插入节点

我们获得某个Dom 节点,假设这个dom节点是空的,我们通过 innerHTML 就可以增加一个元素,如果原本存在元素会覆盖

追加

<pi id="js">JAVAScript</pi>

<div id="list">
    <p id="se">JavaSE</p>
    <p id="ee">JavaEE</p>
    <p id="me">JavaME</p>

</div>

<script>


    let
        js   = document.getElementById("js"),
        list = document.getElementById("list");

    list.appendChild(js);

</script>

效果:


创建新标签插入

<pi id="js">JAVAScript</pi>

<div id="list">
    <p id="se">JavaSE</p>
    <p id="ee">JavaEE</p>
    <p id="me">JavaME</p>

</div>

<script>
// 通过 JS 创建新节点
var newP = document.createElement('p'); // 1. 创建一个 p 标签

newP.id = "newP"; // 2. 给新标签赋id 名字
newP.innerText ="hello XXZ"; //  3. 编辑 p 标签内容

list.appendChild(newP); // 4. 追加到 list 节点
    // 创建一个标签节点
    let myScript = document.createElement("script");
    myScript.setAttribute('type','text/javaScript');
    // myScript.type= 'text/javaScript';  等价公用写法
    
        // 可以创建一个 Style 标签
    let myStyle = document.createElement('style'); // 1. 创建一个 style 标签
    myStyle.setAttribute('type','text/css'); // 2. 给标签创建 type
    myStyle.innerHTML = 'body{ background-color: cyan;}'; // 3. 设置style
    document.getElementsByTagName('head')[0].appendChild(myStyle) // 4. 将设置好的 style 放入 head 中

    // 插入节点
    let ee = document.getElementById('ee');
    let list = document.getElementById('list');
    let js = document.getElementById("js");
    // 插入节点 : (插入谁,参照物)
    list.insertBefore(js,ee);
    
    
</script>

7.——操作表单 (验证)

表单是什么 form DOM 树

  • 文本框 text
  • 下拉框
posted @ 2022-08-28 16:34  项晓忠  阅读(36)  评论(0)    收藏  举报