<head..."/>

JavaScript 基础

467次阅读
没有评论

共计 41364 个字符,预计需要花费 104 分钟才能阅读完成。

JavaScript 基础知识

一、Hello World

1.script 标签

JavaScript 程序可以在 <script> 标签帮助下插入到 HTML 文档的任何地方。

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>
 <body>
     <p> script 标签之前</p>
     <script>
         alert("Hello world!");
     </script>
     <p>...script 标签之后</p>
 </body>
 </html>

<script> 标签中包裹了 JavaScript 代码,当浏览器遇到 <script> 标签,代码会自动运行

2.外部脚本

如果有大量的 JavaScript 代码,可以将它放入一个单独的文件,脚本文件可以通过 src 特性添加到 HTML 文件中。

 <script src="/path/to/script.js"></script>

这里 /path/to/script.js 是脚本文件从网站根目录开始的绝对路径,也可以提供当前页面的相对路径,

src="script.js" 表示当前文件夹中的 script.js 文件。

也可以提供一个完整的 URL 地址,例如:

 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

若要附加多个脚本,则要使用多个标签:

 <script src="/js/script1.js"></script>
 <script src="/js/script2.js"></script>
 ...

注意:

  • 一般会将复杂的脚本放入单独的文件中,使用独立文件的好处是浏览器会下载它,每次使用都从缓存拿出,而不是重新下载,可节省流量和加快浏览速度。
  • 一个单独的 <script> 标签不能同时有 src 特性和内部包裹的代码。 <script src=”file.js”>
         alert(1); //此内容会被忽视
     </script>

二、代码结构

语句

语句是执行行为的语法结构和命令。

我们可以在代码中编写任意数量的语句,语句之间可以使用分好进行分割。

 <script>
     alert("Hello");
     alert("World");   //通常每条语句独占一行
 </script>

分号

当存在换行符时,在 大多数情况 下可以省略分号。建议在语句之间加分号以避免不必要的错误。

 <script>
     alert("Hello")
     alert("World")    //这也是可运行的
 </script>

在这,JavaScript 将换行符理解成隐式分号。这也被称为 自动分号插入。

注释

单行注释以两个正斜杠字符 // 开始。

多行注释以一个正斜杠和星号开始,并以一个星号和一个正斜杠结束 /*注释*/ 。

三、变量

变量

变量 是数据的 “命名储存” 。可以用变量来保存商品、访客等信息。

在 JavaScript 中创建一个变量,需要用到 let 关键字。

 let message;
 message = "Hello";    //保存字符串
 alert(message);   //显示变量内容
 ​
 //可以在一行中声明多个变量
 let user = "John", age = 25 , message = 'Hello';
 ​
 //为了方便阅读,通常一行只声明一个变量
 let user = 'John';
 let age = 25;
 //或者
 let user = 'John'
 , age = 25;

在较旧的脚本中,可能会出现 var ,这与 let 大体相同,目前只需了解。

变量命名

JavaScript 的变量命名有两个限制:

1、变量名称必须仅包含字母、数字、符号 $ 和 _ 。

2、首字符必须非数字。

如果命名包含多个单词,通常采用驼峰式命名法,即除了第一个单词,其他每个单词都以大写开头:myVeryLongName.

常量

声明一个常数不变的变量,可以使用 const:

const myBirthday = '23.01.2002';

const 不能被修改

大写形式的常数

一个普遍的做法是将常量用作别名,以便记住那些在执行之前就已知的难以记住的词。

例如,以十六进制格式为颜色声明常量:

const COLOR_RED = "#F00";
const COLOR_GREEN = "0F0";
const COLOR_BLUE = "00F";

//选择一个颜色
let color = COLOR_BLUE;
alert(color);	//#00F

好处:

  • COLOR_BLUE 比 00F 好记,也更易输入
  • 阅读代码时 COLOR_BLUE 也更易看懂

一般在大写形式的常数用在执行之前就已知的值。

正确命名变量

一些可以遵循的规则:

  • 使用易读的命名,例如 userName 或 shoppingCart
  • 离例如 a、b、c 这种缩写远一些
  • 术语要和团队保持一致,如果网站的访客为 “用户” ,则采用相关的变量命名,比如 currentUser 或者newUser , 而不要使用 currentVisitor 或 newManInTown

四、数据类型

Number 类型

let n = 123;
n = 12.345;

number 类型代表整数和浮点数,除了常规数字,还存在一些 “特殊数值”。

  • Infinity 代表 无穷大 ,可以通过除以 0 来得到它。alert(1 / 0); // Infinity //或者在代码中直接使用它 alert(Infinity);
  • NaN 代表一个 计算错误 ,他是一个不正确的或者未定义的数学操作所得到的结果,例:alert(“not a number” / 2); // NaN // NaN 是粘性的,任何对 NaN 的进一步操作都会返回 NaN alert(“not a number” / 2 + 3); // NaN

BigInt 类型

在 JavaScript 中,number 类型无法表示大于(2^53 -1)或小于 -(2^53 -1)的整数,

有时我们需要很大的数字,例如用于加密或微秒精度的时间戳。

BigInt 类型用于表示任意长度的 整数,也可以通过将 n 附加到整数字段的末尾来创建 BigInt 值。

// 尾部的 n 表示这是一个 BigInt 类型
const bigInt = 123456789684683784887357357324654676n;

String 类型

JavaScript 中的字符串必须被括在引号里,有三种包含方式:

1.双引号:“Hello”

2.单引号: ‘Hello’

3.反引号: `Hello`

反引号是 功能扩展 引号,它们允许我们通过将变量和表达式包装在 ${...} 中,来将它们嵌入到字符串中。例:

let name = "John";

//嵌入一个变量
alert( `Hello, &{name}!` );	// Hello, John!

//嵌入一个表达式
alert( `the result is ${1 + 2}` );	// the result is 3

&{...} 内的表达式会被计算,计算结果会成为字符串的一部分,可以在 &{...} 内放置任何东西。

Boolean 类型(逻辑类型)

null 值

undefined 值

undefined 的含义是 未被赋值

object 类型和 symbol 类型

object 类型是一个特殊的类型,其他所有类型都被称为“原始类型”,因为它们的值只包含一个内容,而 object 则用于储存数据集合和更复杂的实体。

symbol 类型用于创建对象的唯一标识符。

typeof 运算符

typeof 运算符返回参数的类型,它支持两种语法形式:

1.作为运算符: typeof x

2.函数形式: typeof(x)

typeof undefined	 // "undefined"
typeof 0			// "number"
typeof 10n			// "bigint"
typeof true			// "boolean"
typeof "foo"		// "string"
typeof Symbol("id")	 // "symbol"
typeof Math			// "object"
typeof null			// "object"
typeof alert		// "function"

五、交互:alert、prompt 和 confirm

alert

它会显示一条信息,并等待用户按下 “OK”。例如:

alert("Hello");

弹出的这个带有信息的小窗口被称为 模态窗。“modal” 意味着用户不能与页面的其他部分进行交互,直到他们处理完窗口。

prompt

prompt 函数接收两个函数:

result = prompt(title, [default]); 		// default 旁的方括号表示该参数是可选的,不是必需的

浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定 / 取消按钮。

title :显示给用户的文本。

default : 可选的第二个参数,指定 input 框的初始值。

访问者可以在提示输入栏中输入内容,我们在 result 中获取该文本,若用户取消输入,则返回 null。

let age = prompt('How old are you?', 100);
alert('You are &{age} years old!');		//You are 100 years old! (默认值)

confirm

result = confirm(question);

confirm 函数显示一个带有 question 以及确定和取消两个按钮 的模态窗口。

点击确定返回 true ,点击取消返回 false。

let isBoss = confirm('Are you the boss?');
alert( isBoss );		//若按下确定,则显示 true

六、类型转换

字符串转换

let value = true;
alert(typeof value);	// boolean

value = String(value);	// "true"
alert(typeof value);	//string

数字型转换

在算术函数和表达式中,会自动的进行 number 类型转换。

比如:当吧除法 “ / ” 用于非 number 类型:

alert("6" / "2");		// 3, string 类型的值被自动转换成 number 类型后进行计算 

我们也可以使用 Number(value) 显式的将这个 value 转换为 number 类型。

let str = "123";
alert(typeof str);	//string

let num = Number(str);	// 变为 number 类型 123
alert(typeof num);		// number

如果 Number(str) 内 str 不是一个有效的数字,则会返回 NaN。

number 类型转换规则:

值 变成……
undefined NaN
null 0
true 和 false 1 and 0
string 去掉首尾空格后的纯数字字符串中含有的数字。如果剩余字符为空,则转换结果为 0 。否则将会从剩余字符串中 “读取” 数字,当类型转换出现 error 时,返回 NaN

例:

alert(Number("   123"   ));		// 123
alert(Number("123z"));			// NaN(从字符串读取数字,读到 "z" 时出现错误)
alert(Number(true));			// 1
alert(Number(false));			// 0

布尔型转换

转换规则如下:

  • 直观上为 “空” 的值(如 0 、空字符串、null、undefined 和 NaN)将变成 false
  • 其他值变成 ture

比如:

alert(Boolean(1));			// true
alert(Boolean(0));			// false
				
alert(Boolean("hello"));	// true
alert(Boolean(""));			//false

alert(Boolean("0"));		// true
alert(Boolean(" "));		// true (任何非空字符串都是 true)

七、基础运算符-数学

术语:“一元运算符”, “二元运算符”, “运算元”

  • 运算元:例: 5 * 2,左运算元 5,右运算元 2。
  • 如果一个运算符对应只有一个运算元,那么它是 一元运算符
let x = 1;

x = -x;
alert(x);  // -1,一元运算符 负号 生效
  • 如果一个运算符拥有两个运算元,那么它是 二元运算符
let x = 1, y = 3;
alert(y - x);		// 2, 二元运算符减号做减运算

数学

  • 加法 +
  • 减法 -
  • 乘法 *
  • 除法 /
  • 取余 %
  • 求幂 **

用二元运算符 + 连接字符串

let s = "my" + "string";
alert(s);				// mystring

// 只要一个运算符是字符串,那么另外的运算元也会被转化为字符串
alert( '1' + 2 );		//"12"
//运算符是按顺序工作的
alert(2 + 2  + '1');	// "41" 不是 "221"

二元 + 是唯一支持字符串的运算符,其他算术运算符只对数字起作用。

alert(6 - '2');		// 4
alert('6' / 2);		// 3

数字转化,一元运算符 +

若加号应用于单个值,对数字没有任何作用,但 对其他运算元,加号会将其 转化为数字。

//无效
let x = 1;
alert(+x);		// 1

//转化非数字
alert(+true);	// 1
alert(+"");		// 0

//在二元运算符加号起作用之前,所有的值都被转化为了数字
let apples = "2";
let orange = "3";
alert(+apples + +oranges);		// 5

赋值运算符

赋值 = 返回一个值
let a = 1;
let b = 2;

let c = 3 - (a = b + 1);
alert(a);		// 3
alert(c);		// 0
链式赋值
let a, b, c;
a = b = c = 2 + 2;
alert(a);	// 4
alert(b);	// 4
alert(c);	// 4

原地修改

let n = 2;
n += 5;		// n = 7
n *= 2;		// n = 14

自增 / 自减

let counter = 2;
counter++;		// 3
counter--;		// 2

位运算符

  • 按位与 &
  • 按位或 |
  • 按位异或 ^
  • 按位非 ~
  • 左移 <<
  • 右移 >>
  • 无符号右移 >>>

八、空值合并运算符 ‘??’

a ?? b 的结果是:

  • 如果 a 是已定义的, 则结果为 a
  • 如果 a 不是已定义的,则结果为 b

即如果第一个参数不是 null / undefined 则 ?? 返回第一个参数,否则返回第二个参数。

// 显示第一个已定义的值
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
alert(firstName ?? lastName ?? nickName ?? "Anonymous");		// Supercoder

与 || 比较

// 显示第一个真值
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
alert(firstName ?? lastName ?? nickName ?? "Anonymous");		// Supercoder

九、箭头函数

let func  = (arg1, arg2, ...argN) => expression;

// 等同于

let func = function(arg1, arg2, ...argN){
	return expression;
}
  • 如果只有一个参数,可以省略掉参数外的圆括号
let double = n => n * 2;
  • 如果没有参数,应该保留括号
let sayHi = () => alert("Hello!");
  • 可以像函数表达式一样使用
let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
    () => alert('Hello'):		// 若大于等于18
	() => alert('Greetings!');	// 若小于18
welcome();

代码质量

JavaScript 代码规范及调试:

https://zh.javascipt.info/debugging-chrome

使用 Mocha 进行自动化测试

Mocha 中文网:

https://mochajs.cn/

Mocha 实例教程:

https://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html

开发 “pow” : 规范

在创建函数 pow 的代码之前,我们应该想象函数应该做什么并且描述出来。

这样的描述被称作 规范,包含用例的描述以及针对他们的测试。

describe("pow", function() {
    
    it("raises to n-th power", function() {
        assert.equal(pow(2, 3), 8);
    });
});

一个规范包含三个主要的模块:

describe("title", function(){ ... })

表示我们正在描述的功能是是什么。

it("use case description", funtion() { ... })

it 里面的描述部分,以一种 易于理解 的方式描述特定的用例,第二个参数是用于对其进行测试的函数。

assert.equal(value1, value2)

it 块中的代码,如果实现是正确的,它应该在执行的时候不产生任何错误。

assert.* 函数用于检查函数(用例中为 pow)是否按照预期工作。在用例中我们使用了其一的

assert.equal 它会对参数进行比较,如果他们不相等则会抛出一个错误,这里他检查了 pow(2, 3) 的值是否等于 8

开发流程

1.编写初始规范,测试最基本的功能。

2.创建一个最初始的实现。

3.检查它是否工作,运行测试框架 Mocha 来运行测试,当功能未完成时,将显示错误,我们将持续修正直到一切都能工作。

4.现在我们拥有一个带有测试的能工作的初步实现。

5.增加更多的用例到规范中。

6.回到第 3 步,更新程序直到测试不会抛出错误。

7.重复 3 – 6 步,直到功能完善。

行为规范

  • Mocha —— 核心框架,提供了包括通用型测试函数 describe 和 it ,以及用于测试的主函数。
  • Chai —— 提供很多断言支持的库
  • Sinon —— 用于监视函数、模拟内置函数和其他函数的库
<!DOCTYPE html>
<html>
<head>
  <!-- add mocha css, to show results -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css">
  <!-- add mocha framework code -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
  <script>
    mocha.setup('bdd'); // minimal setup
  </script>
  <!-- add chai -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
  <script>
    // chai has a lot of stuff, let's make assert global
    let assert = chai.assert;
  </script>
</head>

<body>

  <script>
    function pow(x, n) {
      /* function code is to be written, empty now */
    }
  </script>

  <!-- the script with tests (describe, it...) -->
  <script src="test.js"></script>

  <!-- the element with id="mocha" will contain test results -->
  <div id="mocha"></div>

  <!-- run tests! -->
  <script>
    mocha.run();
  </script>
</body>

</html>

该页面分为五个部分:

1.<head> —— 添加用于测试的第三方库和样式文件。

2.<script> 包含测试函数,在我们的例子中为和 pow 相关的代码。

3.测试代码 —— 在案例中是名为 test.js 的脚本,它包含 describe("pow", ...) 。

4.HTML 元素 <div id = "mocha"> 将被 Mocha 用来输出结果。

5.可以使用 mocha.run() 命令来开始测试。

改进规范

有时会发现测试通过了,但函数是错误的,这表明我们的规范不完善,需要添加更多的测试用例。

1.第一种 —— 在同一个 it 中再添加一个 assert :

describe("pow", function() {

  it("raises to n-th power", function() {
    assert.equal(pow(2, 3), 8);
    assert.equal(pow(3, 4), 81);
  });

});

2.第二种 —— 写两个测试:(推荐)

describe("pow", function() {

  it("2 raised to power 3 is 8", function() {
    assert.equal(pow(2, 3), 8);
  });

  it("3 raised to power 4 is 81", function() {
    assert.equal(pow(3, 4), 81);
  });

});

改进实现

function pow(x, n) {
  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }
  return result;
}

为了确保函数可以很好的工作,除了手动的编写 it 代码块,我们可以用 for 循环生成他们:

describe("pow", function() {

  function makeTest(x) {
    let expected = x * x * x;
    it(`${x} in the power 3 is ${expected}`, function() {
      assert.equal(pow(x, 3), expected);
    });
  }

  for (let x = 1; x <= 5; x++) {
    makeTest(x);
  }
    
});

嵌套描述

辅助函数 makeTest 应该和 for 组合在一起,只有在 for 中才需要 mackTest 函数。

使用嵌套的 describe 来进行分组:

describe("pow", function() {

  describe("raises x to power 3", function() {

    function makeTest(x) {
      let expected = x * x * x;
      it(`${x} in the power 3 is ${expected}`, function() {
        assert.equal(pow(x, 3), expected);
      });
    }

    for (let x = 1; x <= 5; x++) {
      makeTest(x);
    }

  });

  // ……可以在这里写更多的测试代码,describe 和 it 都可以添加在这。
});

Object(对象):基础知识

一、对象

我们可以使用带有可选 属性列表 的花括号 {...} 来创建对象。一个属性就是一个键值对(key : value)。

我们可以用下面两种语法来创建一个空的对象:

let user = new Object();		//构造函数的语法	
ler user = {};				//字面量的语法

文本和属性

我们可以在创建对象的时候,将一些属性以键值对的形式放到 {…} 中。

let user = {		//一个对象
    name: "John",	//键 "name", 值 "Johhn"
    age: 30			//键 "age", 值 "30"
};

我们可以随时添加、删除和读取文件,可以使用点符号访问属性值:

//读取文件的属性
alert( user.name );		//John
alert( user.age );		//30

//添加布尔类型属性
user.isAdmin = ture;
//用 delete 操作符移除属性
delete user.age;

//可以用多字词语作为属性名,但必须加上引号
let user = {
    "likes birds": true
};

注意:使用 const 声明的对象是可以被修改的

const user = {
    name: "John"
};
user.name = "Pete";
alert(user.name);	// Pete

方括号

对于多词属性,点操作就不能用了,此时有另一种方法,就是使用方括号,可以用于任何字符串。

let user = {};
user["likes birds"] = true;
alert(user["likes birds"]);
delete user["likes birds"];

方括号同时提供了一种通过任意表达式来获取属性名的方法

let key = "likes birds";
user[key] = true;

//变量 key 也可以是用户输入得到的
let user = {
    name: "John",
    age: 30
};
let key = prompt("What do you want to know about the user?", "name");
//访问变量
alert( user[key] );	//John (如果输入 "name")
计算属性

当创建一个对象时,我们可以在对象字面量中使用方括号,这叫做 计算属性。

let fruit = prompt("Which fruit to buy?", "apple");
let bag = {
    [fruit]: 5,
};
alert( bag.apple );		//5, (如果fruit="apple")

我们可以在方括号中使用更复杂的表达式

let fruit = "apple";
let bag = {
    [fruit + "Computers"]: 5	//bag.appleComputers = 5
};

属性值简写

在实际开发中,通常用已存在的变量当做属性名。

function makeUser(name, age) {
    return {
        name: name,
        age: age,
        //...其他属性
    };
}
let user = makeUser("John", 30);
alert(user.name);		//John

有一种更特殊的 属性值缩写 方法,使属性名变得更短。

function makeUser(name, age) {
    return {
        name,		//与 name: name 相同
        age,
        //...
    }
}

属性名称限制

对象的属性名不受保留字的限制 (除了_proto_)

let obj = {
    for: 1, 
    let: 2,
    return: 3
};
alert( obj.for + obj.let + obj.return );		// 6

属性名可以是任何字符串或者 symbol,其他类型会自动转换为字符串。

let obj = {
    0: "test"		// 等同于 "0": "test"
};
alert( obj["0"] );	// test
alert( obj[0] );	// test

属性存在性测试,“in” 操作符

JavaScript 的对象能够被访问任何属性,属性不存在只会得到 undefined。

这里我们可以用 == 来判断,也可以用操作符 “in”

"key" in object

// 例:
let user = {
    name: "John",
    age: 30
};
alert( "age" in user );		// true
alert( "blabla" in user );	// false

注意: in 的左边必须是属性名,如果省略引号,意味着左边是一个变量

let user = {
    age: 30
};
let key = "age";
alert( key in user );	// true, 属性 "age" 存在

“for…in” 循环

for (key in object) {
	// 对此对象属性中的每个键执行的代码
}

// 例如,列出 user 所有的属性
let user = {
    name: "John",
    age: 30,
    isAdmin: true
};
for (let key in user) {
    // keys
    alert( key );	// name, age, isAdmin
    // 属性键的值
    alert( user[key] );	// John, 30, true
}
像对象一样排序

对象有特别的顺序:整数属性会被进行排序,其他属性则按照创建的顺序显示。

let codes = {
    "49": "Germany",
    "41": "Switzerland",
    "44": "Great Britain",
    //...
    "1": "USA"
};
for(let code in codes) {
    alert(code);	//1, 41, 44, 49
}

所以为了解决某些问题,我们可以使用非整数属性来欺骗程序。

let codes = {
    "+49": "Germany",
    "+41": "Switzerland",
    "+44": "Great Britain",
    //...
    "+1": "USA"
};
for (let code in codes) {
    alert( +code );		// 49, 41, 44, 1
}

二、对象引用和复制

与原始类型相比,对象的根本区别之一就是对象是 通过引用 被储存和复制的,而原始类型始终是以 ”整体值“ 的形式被复制的。

let message = "Hello";
let phrase = massage;
// 这样就有了两个独立的变量,都储存着字符串 "Hello"

赋值了对象的变量存储的不是对象本身,而是该对象在 “内存中的地址”

当一个对象变量被复制,引用则被复制,而该对象并没有被复制

let user = { name: "John"};

let admin = user;		// 复制引用

这里仍然只有一个对象,但有两个引用它的变量。

我们可以通过其中任意一个变量来访问该对象并修改它的内容。

let user = { name: 'John'};
let admin = user;

admin.name = 'Pete';		//通过 "admin" 引用来修改
alert( user.name );			// "Pete", 修改能通过 "user" 引用看到

克隆与合并, Object.assign

如果我们想要复制一个对象,则需要创建一个新对象,并通过遍历现有属性的结构,在原始类型的层面,将其复制到新对象,以此复制已有对象的结构。

let user = {
    name: "John", 
    age: 30
};
let clone = {};		// 新的空对象

// 将 user 中所有的属性拷贝到其中
for (let key in user) {
    clone[key] = user[key];
}

// 现在 clone 是带有相同内容的完全独立的对象
clone.name = "Pete";
alert( user.name );		// user 中的 name 属性仍然是 John

我们也可以使用 Object.assign 方法来达成同样的效果

Object.assign(dest, [src1, src2, src3...])
  • 第一个参数 dest 是指目标对象
  • 更后面的参数 src1, …, srcN 是源对象
  • 该方法将所有源对象的属性拷贝到目标对象 dest 中,
  • 调用结果返回 dest

例如,用它来合并多个对象

let user = { name: "John" };
let permission1 = { canView: true };
let permission2 = { canEdit: true };

//将 permission1 和 permission2 中的所有属性都拷贝到 user 中
Object.assign(user, permission1, permission2);
//现在 user = { name: "John", canView: true, canEdit: true };

如果被拷贝的属性名已经存在,那么它会被覆盖。

我们也可以用 Object.assign 代替 for...in 循环来进行简单克隆:

let user = {
    name: "John",
    age: 30
};

//将 user 中的所有属性拷贝到一个空对象中,并返回这个新的对象
let clone = Objectl.assign({}, user);

深层克隆

属性可以是对其他对象的引用,这样应该怎样处理他们呢。

let user = {
    naem: "John", 
    sizes: {
        height: 182,
        width: 50
    }
};

alert( user.sizes.height );		//182

let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes );	//true, 同一个对象

// user 和 clone 分享同一个 sizes
user.sizes.width++			// 通过其中一个改变属性值
alert( cloen.sizes.width );	// 51, 能从另外一个看到变更的结果

为了解决此问题,我们应该使用会检查每个 user[key] 的值的克隆循环,如果值是一个对象,那么也要复制他的结构,这就是深拷贝。

我们可以用递归实现,或者使用 JavaScript 库的 lodash 的 _.cloneDeep(obj)。

JavaScript 的拷贝(克隆)问题

https://blog.csdn.net/qq_37288477/article/details/86652250

三、垃圾回收

可达性

JavaScript 中主要的内存管理概念是 可达性。

1.这里列出固有的可达值的基本集合,这些值 不能被释放

  • 当前函数的局部变量和参数
  • 嵌套调用时,当前调用链上所有函数的变量与参数
  • 全局变量

这些值被称作 根

2.如果一个值可以通过引用或引用链从根访问任何其他值,则认为该值是可达的。JavaScript 引擎中有个称作 垃圾回收器 的东西在后台执行,它监控所有对象的状态,并删除掉那些已经不可达的。

简单的例子
//user 具有对这个对象的引用
let user = {
    name: "John"
};

// 重写 user
user = null;
// 现在 John 变成不可达的了,因为没有引用,垃圾回收器会认为它是垃圾数据并进行回收,然后释放内存
两个引用
// user 具有对这个对象的引用
let user = {
    name: "John"
};
// 把 user 的引用扶着给 admin
let admin = user;

// 重写 user
user = null;
/* 对象仍然可以通过 admin 这个全局变量访问到,所以对象还在内存中,但如果我们又重写了 admin ,对象就会被删除 */

四、对象方法 “this”

方法示例

作为对象属性的函数被称为 方法。

也可以使用预先声明的函数作为方法:

let user = {
    //...
};

// 声明函数
function sayHi() {
    alert("Hello!";)
};

// 然后将其作为一个方法添加
user.sayHi = sayHi;

user.sayHi();	// Hello!

方法简写

user = {
    sayHi: function() {
        alert("Hello");
    }
};

let user = {
    sayHi() {	// 与 "sayHi: function()" 一样
        alert("Hello");
    }
};

方法中的 “this”

通常,对象需要访问对象中存储的信息才能完成其工作。

例如 user.sayHi() 中的代码可能需要用到 user 的 name 属性。

为了访问该对象,方法中可以使用 this 关键字。

let user = {
    name: "John",
    age: 30,
    
    sayHi() {
        // this 指的是当前的对象
        alert(this.name);
	}
};

user.sayHi();		// John

“this” 不受限制

在 JavaScript 中,this 关键字可以用于任何函数,即使它不是对象的方法。

function sayHi() {
    alert( this.name );
}

this 的值是在代码运行时计算出来的,它取决于代码上下文。

let user = { name: "John" };
let admin = { name: "Admin" };

function sayHi() {
    alert( this.name );
}

// 在两个对象中使用相同的函数
user.f = sayHi;
admin.f = sayHi;

// 这两个调用有不同的 this 值
// 函数内部的 "this" 是 “点符号” 前面的那个对象
user.f();	// John ( this == user )
admin.f();	// Admin ( this == admin)

箭头函数没有自己的 “this”

箭头函数有些特别:他们没有自己的 this 。如果在这函数中引用 this ,this 值取自外部 ”正常的函数“。

例:这里的 arrow() 使用的 this 来自外部的 user.sayHi() 方法

let user = {
    firstName: "Ilya",
    sayHi() {
        let arrow = () => alert( this.firstName );
        arrow();
    }
};

user.sayHi();	// Ilya

若我们并不想要一个独立的 this ,想从 外部上下文 中获取时,可以使用箭头函数。

五、构造器和操作符 “new”

常规的 {...} 语法允许创建一个对象,但是我经常需要创建许多类似的对象,例如多个用户或菜单项等。

这里可以使用构造函数和 new 操作符来实现。

构造函数

构造函数在技术上是常规函数,但有两个规定:

1.他们的命名以 大写字母开头。

2.他们 只能由 new 操作符 来执行。

function User(name) {
    this.name = name;
    this.isAdmin = false;
}

let user = new User("Jack");

alert(user.name);	// Jack
alert(user.isAdmin);	// false

当一个函数被使用 new 操作符执行时,它按照以下步骤:

1.一个新的空对象别创建并分配给 this。

2.函数体执行,通常会修改 this ,为其添加新的属性。

3.返回 this 的值。

构造器的 return

  • 如果 return 返回的是一个对象,则返回这个对象,而不是 this
  • 如果 return 返回的是一个原始类型,则忽略

换句话说,带有对象的 return 返回该对象,在所有其他情况下返回 this.

例:这里 return 通过返回一个对象覆盖 this

function BigUser() {
    this.name = "John";
    return { name: "Godzilla" };	// 返回这个对象
}
alert( new BigUser().name );	// Godzilla ,得到了那个对象

// return 为空的例子
function SmallUser() {
    this.name = "John";
    return;		// 返回 this
}
alert( new SmallUser().name );		// John 

构造器中的方法

使用构造器函数来创建对象会带来很大的灵活性。

例:

function User(name) {
  this.name = name;

  this.sayHi = function() {
    alert( "My name is: " + this.name );
  };
}

let john = new User("John");

john.sayHi(); // My name is: John

六、可选链 “?.”

“不存在的属性” 的问题

假设我们有很多个 user 对象,其中存储了我们的用户数据。

大多数用户的地址都存储在 user.address 中,街道地址存储在 user.address.street 中,但有些用户没有提供这些信息,在这种情况下,当我们尝试获取 user.address.street, 而该用户恰好没有提供信息,我们则会收到一个错误:

let user = {};	// 一个没有 "address" 属性的 user 对象
alert( user.address.street );	// Error

我们希望避免出现这种错误,而是接收 null 作为结果。

最先想到的是使用 if 或 ? 对该值进行检查:

let user = {};
alert( user.address ? user.address.street : undefined);

这虽然可以避免错误,但是对于多重嵌套的属性就会出现更多重复的代码,于是可以使用 && 运算符:

let user = {};
alert( user.address && user.address.stree && user.street.name );	// undefined (不报错)

依次对整条路径属性使用与运算进行了判断,确保所有节点是存在的,但仍不够优雅,于是 可选链 ?. 被加入到了 JavaScript中。

可选链

如果可选链 ?. 前面的部分是 undefined 或者 null ,它会停止运算并返回该部分。

例如 value?.prop:

  • 如果 value 存在,则结果与 value.prop 相同
  • 否则(当 value 为 undefined / null 时)则返回 undefined

下面是一种使用 ?. 安全的访问 user.address.street 的方式:

let user = {};		// user 没有 address 属性
alert( user?.address?.street );		// undefined (不报错)

注意:?. 语法使其前面的值成为可选值,但不会对其后面起作用。

例如:在 user?.address.street.name 中,?. 允许 user 为 null / undefined,但仅此而已。

注意:

  • 不要过度使用可选链应该只讲 ?. 使用在一些 可以不存在 的地方。
  • ?. 前的变量必须已声明如果未声明变量 user ,那么 user.anything 会触发一个错误 ReferenceError: user is not defined。

短路效应

如果 ?. 左边部分不存在,就会立即停止运算(短路效应)

let user = null;
let x = 0;
user?.sayHi(x++);	// 没有 "sayHi" ,因此代码没有执行 x++
alert(x);		// 0

其他变体: ?.(), ?.[]

可选链是一个特殊的语法结构,还可以与函数和方括号一起使用

let userAdmin = {
    admin() {
        alert("I am admin");
    }
};
let userGuest = {};
userAdmin.admin?.();	// I am admin
userGuest.admin?.();	// 什么都没有

此外,我们还可以将 ?. 跟 delete 一起使用

delete user?.name;		// 如果 user 存在,则删除 user.name

七、Symbol 类型

Symbol

“Symbol” 值表示唯一的标识符

可以使用 Symbol() 来创建这种类型的值,创建时,可以给 Symbol 一个描述:

// id 是描述为 "id" 的Symbol, id 是 Symbol 的一个实例化对象
let id = Symbol("id");

// Symbol 保证是唯一的
let id1 = Symbol("id");
let id2 = Symbol("id");
alert( id1 == id2 )		// false

隐藏属性

Symbol 允许我们创建对象的 “隐藏”属性,代码的任何其他部分不能意外访问或重写这些属性。

let user = {		// 属于另一个代码
    name: "John";
};
let id = Symbol("id");
user[id] = 1;
alert( user[id] );		// 使用 Symbol 作为键来访问

对象字面量中的 Symbol

如果我们要在对象字面量 {...} 中使用 Symbol,则需要用方括号括起来。

let id = Symbol("id");
let user = {
    name: "John",
    [id]: 123		// 不是 "id": 123
};	// 这是因为我们需要变量 id 作为键而不是字符串

Symbol 在 for…in 中会被跳过

let id = Symbol("id");
let user = {
  name: "John",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name, age (no symbols)

// 使用 Symbol 任务直接访问
alert( "Direct: " + user[id] );

全局 symbol

通常所有的 Symbol 都是不同的,但有时我们想要名字相同的 Symbol 具有相同的实体。

为了实现这一点,有一个 全局 Symbol 注册表。我们可以在其中创建 Symbol 并在稍后访问他们。

从注册表中读取(不存在则创建)Symbol,Symbol.for(key)。

// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 Symbol 不存在,则创建它

// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");

// 相同的 Symbol
alert( id === idAgain ); // true

Document

一、浏览器环境,规格

文档对象模型(DOM)

文档对象模型,将所有页面内容表示为可以修改的对象。

document 对象是页面的主要 “入口点” ,我们可以使用它来更改或创建页面上的任何内容。

// 将背景颜色修改为红色
document.body.style.background = "red";
// 在一秒后将其修改回来
setTimeout(() => document.body.style.background = "", 1000);

浏览器对象模型(BOM)

浏览器对象模型,表示由浏览器(主机环境)提供的用于处理文档(document)之外的所有内容的其他对象。

例:

  • navigator 对象提供了有关浏览器和操作系统的背景信息,例如 navigator.platform — 关于平台(帮助区分 Windows / Linuw / Mac 等),navigator.userAgent — 关于当前浏览器。
  • location 对象允许我们读取当前 URL,并且可以将浏览器重定向到新的 URL。
// 使用 location 对象的方法
alert(location.href);		// 显示当前 URL
if (confirm("Go to Wikipedia?")) {
    location.href = "https://wikipedia.org";	// 将浏览器重定向到另一个 URL
}

函数 alert / confirm / prompt 也是 BOM 的一部分。

二、DOM 树

HTML 文档的主干是标签(tag)。

根据文档对象模型(DOM),每个 HTML 标签都是一个对象,嵌套的标签是闭合标签的 ”子标签“,标签内的文本也是一个对象。

例如:document.body 是表示 <body> 标签的对象

document.body.style.background = "red";		// 将背景设置为红色
setTimeout(() => document.body.style.background = '', 3000);	// 恢复

除了用 style.background 来修改 document.body 的背景颜色,还有其他属性,如:

  • innerHTML — 节点的 HTML 内容
  • offsetWidth — 节点的宽度(以像素度量)
  • …

DOM 的例子

<!DOCTYPE HTML>
<html>
    <head>
        <title>About ekl</title>
    </head>
    <body>
        The truth about elk.
    </body>
</html>

DOM 将 HTML 表示为标签的树形结构:

  • HTML
    • HEAD
      • #text ↵␣␣␣␣
      • TITLE
        • #text About elk
      • #text ↵␣␣
    • #text ↵␣␣
    • BODY
      • #text The truth about elk.

标签被称为 元素节点 ,形成了树状结构:<html> 在根节点,<head> 和 <body> 是其子项。

元素内的文本形成 文本节点, 被标记为 #text 。一个文本节点只包含一个字符串,他没有子项,且总是树的叶子。

注意文本节点中的特殊字符:

  • 换行符:↵(在JavaScrip 中为 \n )
  • 空格:␣

空格和换行符都是完全有效的字符,他们形成文本节点并称为 DOM 的一部分。

有两个顶级排除项:

1.<head> 之前的空格和换行符均被忽略。

2.付过在 </body> 后放置一些东西,那么他会被自动移动到 body 内,并处于最下方,因为 HTML 规范要求所有内容必须位于 <body> 内。

自动修正

如果浏览器遇到格式不正确的 HTML ,它会在形成 DOM 时自动更正它。

<p>Hello
<li>Mom
<li>and
<li>Dad

一个没有关闭标签的文档将成为一个正常的 DOM, 因为浏览器在读取标签时会填补缺失的部分:

  • HTML
    • HEAD
    • BODY
      • p
        • #text Hello
      • li
        • #text Mom
      • li
        • #text and
      • li
        • #text Dad

其他节点类型

除了元素和文本节点,还有其他的节点类型,例如注释:

<!DOCTYPE HTML>
<html>
<body>
  The truth about elk.
  <ol>
    <li>An elk is a smart</li>
    <!-- comment -->
    <li>...and cunning animal!</li>
  </ol>
</body>
</html>
  • HTML
    • HEAD
    • BODY
      • #text The truth about elk.
      • ol
        • #text ↵␣␣␣␣␣␣
        • li
          • #text An elk is a smart
        • #text ↵␣␣␣␣␣␣
        • #comment comment
        • #text ↵␣␣␣␣␣␣
        • li
          • #text …and cunning animal!
        • #text ↵␣␣␣␣
      • #text ↵␣␣↵

在这可以看到一个新的树节点类型 — comment node, 被标记为 comment。

HTML中的所有内容,甚至注释,都会称为 DOM 的一部分

一共有 12种节点类型 ,通常用到其中 4 种:

  • document — DOM 的 “入口点”
  • 元素节点 — HTML 标签,树构建块
  • 文本节点 — 包含文本
  • 注释 — 他不会显示,但 JS 可以从 DOM 中读取它

三、搜索:getElement, querySelector

document.getElementById 或者只使用 id

如果一个元素有 id 特性, 我们就可以使用 document.getElementById 方法获取该元素。

<div id="elem">
    <div id="elem=content">Element</div>
</div>

<script>
        // 获取该元素
        let elem = document.getElementById("elem");
		// 将该元素背景改为红色
		elem.style.background = "red";
</script>

document.getElementsByTagName

使用 document.getElementsByTagName 方法可以返回带有指定标签名的 对象的集合

注意:

  • 得到的是一个对象的集合(伪数组),所以需要遍历去访问
  • 得到元素对象是动态的
  • 还可以获取某个元素内部所有指定标签名的子元素

querySelectorAll

它返回 elem 中与给定 CSS 选择器匹配的 所有元素。

例:查找所有为最后一个子元素的 <li> 元素;

<ul>
    <li>The</li>
    <li>test</li>
</ul>
<ul>
    <li>has</li>
    <li>passed</li>
</ul>
<script>
    let elements = document.querySelectorAll('ul > li:last-child');
    
    for(let elem of elements) {
        alert( elem.innerHTML );	// "test", "passed"
    }
</script>
  • 也可以使用伪类例如: document.querySelectorAll(':hover') 会返回鼠标指针现在已经结束的元素的集合(按嵌套顺序:从最外层 <html> 到嵌套最多的元素

querySelector

elem.querySelector(css) 调用会返回给定 CSS 选择器的第一个元素

注意需要加标点 .box #nav

matches

elem.matches(css) 不会查找任何内容,只会检查 elem 是否与给定的 CSS 选择器匹配。它返回 true 或者 false.

当我们遍历元素并想过滤我们不感兴趣的元素时,可以使用这个方法。

<a href="http://example.com/file.zip">...</a>
<a href="http://ya.ru">...</a>

<script>
    for (let elem of document.body.children) {
        if (elem.matches('a[href$="zip"]')) {
            alert("The archive reference: " + elem.href );
        }
    }
</script>

closest

elem.closest(css) 方法会查找与 CSS 选择器匹配的最近的祖先。elem 自己也会被搜索。

<h1>Contents</h1>
<div class="contents">
    <ul class="book">
        <li class="chapter">Chapter 1</li>
        <li class="chapter">Chapter 1</li>
    </ul>
</div>

<script>
    let chapter = document.querySelector('.chapter');	// li
    alert(chapter.closest('.book'));		// ul
    alert(chapter.closest('.contents'));	//div
    
    alert(chapter.closest('h1'));		// null ,h1 不是祖先
</script>

getElementsBy*

  • elem.getElementsByTagName(tag) 查找具有给定标签的元素,并返回他们的集合。tag 参数也可以是对于任何标签的星号 “ * ”。
  • elem.getElenmentsByClassName(className) 返回具有给定 CSS 类的元素
  • document.getElementsByName(name) 返回在文档范围内具有给定 name 特性的元素
// 获取文档中所有 div
let divs = document.getElementsByTagName('div');

实时的集合

所有的 getElementsBy* 方法都会返回一个 实时的集合 。这集合会在文档发生更改时 自动更新。

<div>First div</div>

<script>
    let divs = document.getElementsByTagName('div');
    alert(divs.length);		// 1
</script>

<div>Second div</div>

<script>
    alert(divs.length);		// 2
</script>

相反 querySelectorAll 返回的是一个 静态的集合。

<div>First div</div>

<script>
    let divs = document.querySelectorAll('div');
    alert(divs.length);		// 1
</script>

<div>Second div</div>

<script>
    alert(divs.length);		// 1
</script>

四、节点属性: type, tag 和 content

DOM 节点类

不同的 DOM 节点可能有不同的属性。例如,标签 <a> 相对应的元素节点具有链接相关的(link-related)属性,标签 <input> 相对应的元素节点具有与输入相关的属性,等。这些标签对应的 DOM 节点之间也存在共有的属性和方法,所有类型的 DOM 节点都形成了一个单一层次结构。

每个DOM节点都术语相应的内建类。

层次结构的根节点是 EventTarget,Node 继承自它,其他 DOM 节点继承自 Node。

类如下所示:

  • EventTarget — 是根的“抽象(abstract)”类。该类的对象从未被创建。它作为一个基础,以便让所有 DOM 节点都支持所谓的“事件(event)”,我们会在之后学习它。
  • Node — 也是一个“抽象”类,充当 DOM 节点的基础。它提供了树的核心功能:parentNode,nextSibling,childNodes 等(它们都是 getter)。Node 类的对象从未被创建。但是有一些继承自它的具体的节点类,例如:文本节点的 Text,元素节点的 Element,以及更多异域(exotic)类,例如注释节点的 Comment。
  • Element — 是 DOM 元素的基本类。它提供了元素级的导航(navigation),例如 nextElementSibling,children,以及像 getElementsByTagName 和 querySelector 这样的搜索方法。浏览器中不仅有 HTML,还会有 XML 和 SVG。Element 类充当更多特定类的基本类:SVGElement,XMLElement 和 HTMLElement。
  • HTMLElement — 最终是所有 HTML 元素的基本类。各种 HTML 元素均继承自它:
    • HTMLInputElement — <input> 元素的类,
    • HTMLBodyElement — <body> 元素的类,
    • HTMLAnchorElement — <a> 元素的类,
    • ……等,每个标签都有自己的类,这些类可以提供特定的属性和方法。

我们可以通过回调来查看 DOM 节点类名,因为对象通常都具有 constructor 属性。它引用类的 constructor,constructor.name 就是它的名称:

alert( document.body.constructor.name ); // HTMLBodyElement

……或者我们可以对其使用 toString 方法:

alert( document.body ); // [object HTMLBodyElement]

或使用 instanceof 来检查继承:

alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true

“nodeType” 属性

nodeType 属性提供了另一种用来获取 DOM 节点类型的方法。

  • 对于元素节点 elem.nodeType == 1
  • 对于文本节点 elem.nodeType == 3
  • 对于 document 对象 elem.nodeType == 9

标签:nodeName 和 tagName

给定一个 DOM 节点,我们可以从 nodeName 或者 tagName 属性中读取它的标签名:

alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY

tagName 仅受元素节点支持(因为它起源于 Element 类),而 nodeName 则可以说明其他节点类型。

<body><!-- comment -->

  <script>
    // for comment
    alert( document.body.firstChild.tagName ); // undefined(不是一个元素)
    alert( document.body.firstChild.nodeName ); // #comment

    // for document
    alert( document.tagName ); // undefined(不是一个元素)
    alert( document.nodeName ); // #document
  </script>
</body>

innerHTML: 内容

innerHTML 属性允许将元素中的 HTML 获取为字符串形式。

<body>
  <p>A paragraph</p>
  <div>A div</div>

  <script>
    alert( document.body.innerHTML ); // 读取当前内容
    document.body.innerHTML = 'The new BODY!'; // 替换它
  </script>

</body>

注意:“innerHTML +=” 会进行完全重写

outerHTML: 元素的完整 HTML

outerHTML 属性包含了元素的完整 HTML。

<div id="elem">Hello <b>World</b></div>

<script>
  alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div>
</script>

注意:与 innerHTML 不同,写入 outerHTML 不会改变元素。而是在 DOM 中替换他。

<div>Hello, world!</div>

<script>
  let div = document.querySelector('div');

  // 使用 <p>...</p> 替换 div.outerHTML
  div.outerHTML = '<p>A new element</p>'; // (*)

  // 蛤!'div' 还是原来那样!
  alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>

outerHTML 赋值不会修改 DOM 元素,而是将其从 DOM 中删除并在其位置插入新的 HTML。

在 div.outerHTML=... 中发生的事为:

  • div 被从文档(document)中移除。
  • 另一个 HTML 片段 <p>A new element</p> 被插入到其位置上。
  • div 仍拥有其旧的值。新的 HTML 没有被赋值给任何变量。

nodeValue / data: 文本节点内容

文本节点具有他们的对应项:nodeValue 和 data 属性。

<body>
  Hello
  <!-- Comment -->
  <script>
    let text = document.body.firstChild;
    alert(text.data); // Hello

    let comment = text.nextSibling;
    alert(comment.data); // Comment
  </script>
</body>

textContent: 纯文本

textContent 提供了对元素内的 文本 的访问权限:仅文本,去掉所有 <tags>。

<div id="news">
  <h1>Headline!</h1>
  <p>Martians attack people!</p>
</div>

<script>
  // Headline! Martians attack people!
  alert(news.textContent);
</script>

textContent ,允许以 “安全方式” 写入文本

假设有一个用户输入任意字符串,我们希望将其显示出来

  • 使用 innerHTML,我们将其 “作为 HTML” 插入,带有所有 HTML 标签。
  • 使用 textContent,我们将其 “作为文本” 插入,所有符号均按字面意义处理。
<div id="elem1"></div>
<div id="elem2"></div>

<script>
  let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");

  elem1.innerHTML = name;
  elem2.textContent = name;
</script>

1.第一个 div 获取 name “作为 HTML” :所有标签都变为标签,我们可以看到粗体的 name。

2.第二个 div 获取 name “作为文本” :我们可以从字面上看到 <b>Winner-the-Pooh!</b>

“hidden” 属性

我们可以通过 hidden 指定元素是否可见。

<div>Both divs below are hidden</div>

<div hidden>With the attribute "hidden"</div><!--不可见-->

<div id="elem">JavaScript assigned the property "hidden"</div>
<!--不可见-->

<script>
  elem.hidden = true;
</script>

或者用于作为 blinking 元素:

<div id="elem">A blinking element</div>

<script>
  setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>
<!--A blinking element 这一文本每1秒出现一次,即闪烁效果-->

五、特性和属性

DOM 属性

DOM 节点是常规的 JavaScript 对象。我们可以 alert 它们。

document.body.myData = {
  name: 'Caesar',
  title: 'Imperator'
};

alert(document.body.myData.title); // Imperator

我们还可以修改内建属性的原型,例如修改 Element.prototype 为所有元素添加一个新方法:

Element.prototype.sayHi = function() {
  alert(`Hello, I'm ${this.tagName}`);
};

document.documentElement.sayHi(); // Hello, I'm HTML
document.body.sayHi(); // Hello, I'm BODY

DOM 属性和方法的行为就像常规的 Javascript 对象一样:

  • 它们可以有很多值。
  • 它们是大小写敏感的(要写成 elem.nodeType,而不是 elem.NoDeTyPe)。

HTML 特性

在 HTML 中,标签可能拥有特性(attributes)。当浏览器解析 HTML 文本,并根据标签创建 DOM 对象时,浏览器会辨别 标准的 特性并以此创建 DOM 属性。

所以,当一个元素有 id 或其他 标准的 特性,那么就会生成对应的 DOM 属性。但是非 标准的 特性则不会。

<body id="test" something="non-standard">
  <script>
    alert(document.body.id); // test
    // 非标准的特性没有获得对应的属性
    alert(document.body.something); // undefined
  </script>
</body>

如果一个特性不是标准的,那么就没有相对应的 DOM 属性。

所有特性都可以通过使用以下方法进行访问:

  • elem.hasAttribute(name) — 检查特性是否存在。
  • elem.getAttribute(name) — 获取这个特性值。
  • elem.setAttribute(name, value) — 设置这个特性值。
  • elem.removeAttribute(name) — 移除这个特性。

示例:

<body>
  <div id="elem" about="Elephant"></div>

  <script>
    alert( elem.getAttribute('About') ); // (1) 'Elephant',读取

    elem.setAttribute('Test', 123); // (2) 写入

    alert( elem.outerHTML ); // (3) 查看特性是否在 HTML 中(在)

    for (let attr of elem.attributes) { // (4) 列出所有
      alert( `${attr.name} = ${attr.value}` );
    }
      /* 
      id = elem 
      about = Elephant
      test = 123
      */
  </script>
</body>

非标准的特性,dataset

有时,非标准的特性常常用于将自定义的数据从 HTML 传递到 JavaScript,或者用于为 JavaScript “标记” HTML 元素。

<!-- 标记这个 div 以在这显示 "name" -->
<div show-info="name"></div>
<!-- 标记这个 div 以在这显示 "age" -->
<div show-info="age"></div>

<script>
  // 这段代码找到带有标记的元素,并显示需要的内容
  let user = {
    name: "Pete",
    age: 25
  };

  for(let div of document.querySelectorAll('[show-info]')) {
    // 在字段中插入相应的信息
    let field = div.getAttribute('show-info');
    div.innerHTML = user[field]; // 首先 "name" 变为 Pete,然后 "age" 变为 25
  }
</script>

它们还可以用来设置元素的样式。

例如,这里使用 order-state 特性来设置订单状态:

<style>
  /* 样式依赖于自定义特性 "order-state" */
  .order[order-state="new"] {
    color: green;
  }

  .order[order-state="pending"] {
    color: blue;
  }

  .order[order-state="canceled"] {
    color: red;
  }
</style>

<div class="order" order-state="new">
  A new order.
</div>

<div class="order" order-state="pending">
  A pending order.
</div>

<div class="order" order-state="canceled">
  A canceled order.
</div>

为什么使用特性比使用 .order-state-new,.order-state-pending,.order-state-canceled 这些样式类要好?

因为特性值更容易管理。我们可以轻松地更改状态:

// 比删除旧的或者添加一个新的类要简单一些
div.setAttribute('order-state', 'canceled');

但自定义的特性也存在问题,例如自定义的非标准的特性被引入到了标准中并有了自己的用途。

为了避免冲突,存在 data-* 特性。

所有以 data- 开头的特性均被保留供程序员使用。他们可在 dataset 属性中使用

例如,如果一个 elem 有一个名为 "data-about" 的特性,那么可以通过 elem.dataset.about 取到它。

<body data-about="Elephants">
<script>
  alert(document.body.dataset.about); // Elephants
</script>

六、修改文档

DOM 修改是创建 实时页面 的关键

例子:展示一条消息

我们将在页面上添加一条比 alert 更好看的消息。外观如下:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;		/* 一个绿色的盒子 */
}
</style>

<div class="alert">
  <strong>Hi there!</strong> You've read an important message.
</div>

这是一个 HTML 示例。现在,让我们使用 JavaScript 创建一个相同的 div(假设样式已经在 HTML/CSS 文件中)。

创建一个元素

要创建 DOM 节点,这里有两种方法:

document.createElement(tag)

用给定的标签创建一个新 元素节点(element node):

let div = document.createElement('div');

document.createTextNode(text)

用给定的文本创建一个 文本节点:

let textNode = document.createTextNode('Here I am');

大多数情况下,我们需要为此消息创建像 div 这样的元素节点。

创建一条消息

// 1. 创建 <div> 元素
let div = document.createElement('div');

// 2. 将元素的类设置为 "alert"
div.className = "alert";

// 3. 填充消息内容
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

它到目前为止只是一个变量,尚未在页面中,我们无法在页面上看到它。

插入方法

为了让 div 显示出来,我们需要将其插入到 document 的某处。

对此有一个特殊的方法 document.body.append(div)。

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

  document.body.append(div); //-------------
</script>
  • node.append(...nodes or strings) —— 在 node 末尾 插入节点或字符串,
  • node.prepend(...nodes or strings) —— 在 node 开头 插入节点或字符串,
  • node.before(...nodes or strings) —— 在 node 前面 插入节点或字符串,
  • node.after(...nodes or strings) —— 在 node 后面 插入节点或字符串,
  • node.replaceWith(...nodes or strings) —— 将 node 替换为给定的节点或字符串。

下面是使用这些方法将列表项添加到列表中,以及将文本添加到列表前面和后面的示例:

<ol id="ol">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  ol.before('before'); // 将字符串 "before" 插入到 <ol> 前面
  ol.after('after'); // 将字符串 "after" 插入到 <ol> 后面

  let liFirst = document.createElement('li');
  liFirst.innerHTML = 'prepend';
  ol.prepend(liFirst); // 将 liFirst 插入到 <ol> 的最开始

  let liLast = document.createElement('li');
  liLast.innerHTML = 'append';
  ol.append(liLast); // 将 liLast 插入到 <ol> 的最末尾
</script>
<!--
before
	1.prepend
	2.0
	3.1
	4.2
	5.append
after
-->

节点移除

想要移除一个节点,可以使用 node.remove()。

例:让消息在一秒后消失:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

  document.body.append(div);
  setTimeout(() => div.remove(), 1000);//-----------------------
</script>

注意: 如果我们要将一个元素 移动 到另一个地方,则无需将其从原来的位置中删除。

所有插入方法都会自动从旧位置删除该节点。

克隆节点

调用 elem.cloneNode(true) 来创建元素的一个“深”克隆 — 具有所有特性(attribute)和子元素。如果我们调用 elem.cloneNode(false),那克隆就不包括子元素。

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<div class="alert" id="div">
  <strong>Hi there!</strong> You've read an important message.
</div>

<script>
  let div2 = div.cloneNode(true); // 克隆消息
  div2.querySelector('strong').innerHTML = 'Bye there!'; // 修改克隆

  div.after(div2); // 在已有的 div 后显示克隆
</script>
<!--
Hi there! You've read an important message.
Bye there! You've read an important message.
-->

七、节点操作

1.父节点

node.parentNode
  • parentNode 属性可返回某节点的父节点,(最近的一个父节点)
  • 如果指定节点没有父节点则返回 null

2.子节点

parentNode.childNodes    //(标准)
parentNOde.children 	//(非标准)

注意:parentNode.childNodes 返回值包含所有子节点包括元素节点、文本节点等。

若只想要获得里面的元素节点,则需要专门处理:

var ul = document.querySelector("ul");
for (var i = 0; i < ul.childNodes.length; i++) {
    if (ul.childNodes[i].nodeType == 1) {
        // ul.childNodes[i] 是元素节点
        console.log(ul.childNodes[i])
    }
}
// 包含全部子节点 包括文本
parentNode.firstChild		// 返回第一个子节点
parentNode.lastChild		// 返回最后一个子节点

// 只支持 IE9+
parentNode.firstElementChild	// 返回第一个子元素节点
parentNode.lastElementChild		//  返回最后一个子元素节点

3.兄弟节点

// 包含所有节点包括文本节点
node.nextSibling	// 返回当前元素的下一个兄弟节点
node.previousSibling 	// 返回当前元素上一个兄弟节点

// IE9+
node.nextElementSibling		// 返回当前元素下一个兄弟元素节点
node.previousElementSibling	// 返回当前元素上一个兄弟节点

4.创建节点

// 创建
document.creatElement("tagName");
// 添加
node.appendChild(child)		// 将一个节点添加到指定父节点的子节点列表末尾
node.insertBefore(child,指定元素)	// 将一个节点添加到父节点的指定子节点前面

5.删除节点

node.removeChild(child)

6.复制节点(克隆节点)

node.cloneNode()

注意:

1.如果括号参数为 空或者 false,则是浅拷贝,只复制节点本身,不克隆里面的子节点。

2.如果括号参数为 true,则是深拷贝,会复制节点本身和里面的子节点

7.三种动态创建元素的区别

  • document.write()
  • element.innerHTML
  • document.createElement()

区别

1.doucment.write() 是直接将内容写入页面的内容流,当文档流执行完毕,再执行会导致页面重绘。

2.innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘。

3.innerHTML 创建多个元素效率更高(不拼接字符,采取数组形式拼接),结构略复杂。

4.createElement() 创建多个元素效率稍微较低,但结构跟清晰。

八、DOM 总结

1.创建

  • document.write
  • innerHTML
  • createElement

2.增

  • appendChild
  • insertBefore

3.删

  • removeChild

4.改

  • 修改元素属性: src、href、title 等
  • 修改普通元素内容: innerHTML、innerText
  • 修改表单元素: value、type、disabled 等
  • 修改元素样式: style、className

5.查

  • DOM 提供的 API 方法: getElementById、getElementByTagName
  • H5 提供的方法: querySelector、querySelectorAll
  • 利用节点操作获取元素: parentNode、children、previousElementSibling、nextElementSibling

6.属性操作

  • setAttribute: 设置 dom 的属性值
  • getAttribute: 获取 dom 的属性值
  • removeAttribute: 移除属性

7.事件操作

鼠标事件 触发条件
onclick 鼠标点击触发
onmouseover 鼠标经过触发
onmouseout 鼠标离开触发
onfocus 获得鼠标焦点触发
onblur 失去鼠标焦点触发
onmousemove 鼠标移动触发
onmouseup 鼠标弹起触发
onmousedown 鼠标按下触发

浏览器事件

一、事件简介

事件 是某事发生的信号。所有的 DOM 节点都生成这样的信号(但事件不仅限于 DOM)。

这是最有用的 DOM 事件的列表:

鼠标事件:

  • click —— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
  • contextmenu —— 当鼠标右键点击一个元素时。
  • mouseover / mouseout —— 当鼠标指针移入/离开一个元素时。
  • mousedown / mouseup —— 当在元素上按下/释放鼠标按钮时。
  • mousemove —— 当鼠标移动时。

键盘事件:

  • keydown 和 keyup —— 当按下和松开一个按键时。

表单(form)元素事件:

  • submit —— 当访问者提交了一个 <form> 时。
  • focus —— 当访问者聚焦于一个元素时,例如聚焦于一个 <input>。

Document 事件:

  • DOMContentLoaded —— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。

CSS 事件:

  • transitionend —— 当一个 CSS 动画完成时。

事件处理程序

为了对事件做出相应,可以分配一个 处理程序 — 一个在事件发生时运行的函数。

HTML 特性

处理程序可以设置在 HTML 中名为 on<event> 的特性中。

<input value="Click me" onclick="alert('Click!')" type="button">

在鼠标点击时,onclick 中的代码就会运行。

我们最好在 HTML中调用 JavaScript 函数:

<script>
  function countRabbits() {
    for(let i=1; i<=3; i++) {
      alert("Rabbit number " + i);
    }
  }
</script>

<input type="button" onclick="countRabbits()" value="Count rabbits!">

DOM 属性

我们可以使用 DOM 属性来分配处理程序。

<input id="elem" type="button" value="Click me">
<script>
  elem.onclick = function() {
    alert('Thank you');
  };
</script>

因为这里只有一个 onclick 属性,所以我们无法分配更多事件处理程序。

<input type="button" id="elem" onclick="alert('Before')" value="Click me">
<script>
  elem.onclick = function() { // 覆盖了现有的处理程序
    alert('After'); // 只会显示此内容
  };
</script>

访问元素:this

处理程序中的 this 的值是对应的元素。就是处理程序所在的那个元素。

下面这行代码中的 button 使用 this.innerHTML 来显示它的内容:

<button onclick="alert(this.innerHTML)">Click me</button>

this 指向的是事件函数的 调用者

可能出现的错误

我们可以将一个现存的函数用作处理程序:

function sayThanks() {
  alert('Thanks!');
}

elem.onclick = sayThanks;

注意: 函数应该是以 sayThanks 的形式进行赋值,而不是 sayThanks()。

如果我们添加了括号,那么 sayThanks() 就变成了一个函数调用。所以,最后一行代码实际上获得的是函数执行的 结果。

addEventListener

我们有一种特殊方法 addEventListener 和 removeEnentListenter 来管理处理程序的替代方法。

添加处理程序的语法:

element.addEventListener(event, handler[, options]);

event

事件名,例如:"click"。

handler

处理程序。

options

具有以下属性的附加可选对象:

  • once:如果为 true,那么会在被触发后自动删除监听器。
  • capture:事件处理的阶段。由于历史原因,options 也可以是 false/true,它与 {capture: false/true} 相同。
  • passive:如果为 true,那么处理程序将不会调用 preventDefault()。

移除处理程序:

element.removeEventListener(event, handler[, options]);

多次调用 addEventListener 允许添加多个处理程序:

<input id="elem" type="button" value="Click me"/>

<script>
  function handler1() {
    alert('Thanks!');
  };

  function handler2() {
    alert('Thanks again!');
  }

  elem.onclick = () => alert("Hello");
  elem.addEventListener("click", handler1); // Thanks!
  elem.addEventListener("click", handler2); // Thanks again!
</script>

对于某些事件,只能通过 addEventListener 设置处理程序

例如,DOMContentLoaded 事件,该事件在文档加载完成并且 DOM 构建完成时触发。

// 永远不会运行
document.onDOMContentLoaded = function() {
  alert("DOM built");
};

// 这种方式可以运行
document.addEventListener("DOMContentLoaded", function() {
  alert("DOM built");
});

事件对象

当事件发生时,浏览器会创建一个 event 对象,将详细信息放入其中,并将其作为参数传递给处理程序。

下面是一个从 event 对象获取鼠标指针的坐标的示例:

<input type="button" value="Click me" id="elem">

<script>
  elem.onclick = function(event) {
    // 显示事件类型、元素和点击的坐标
    alert(event.type + " at " + event.currentTarget);
    alert("Coordinates: " + event.clientX + ":" + event.clientY);
  };
</script>

event 对象的一些属性:

event.type

事件类型。

event.currentTarget

处理事件的元素,与 this 相同。

event.clientX / event.clientY

指针事件的指针的窗口相对坐标。

对象处理事件:handleEvent

我们可以使用 addEventListener 将一个对象分配为事件处理程序,当事件发生时,就会调用该对象的 handleEvent 方法。

<button id="elem">Click me</button>

<script>
  let obj = {
    handleEvent(event) {
      alert(event.type + " at " + event.currentTarget);
    }
  };

  elem.addEventListener('click', obj);
</script>

我们也可以对此使用一个类:

<button id="elem">Click me</button>

<script>
  class Menu {
    handleEvent(event) {
      switch(event.type) {
        case 'mousedown':
          elem.innerHTML = "Mouse button pressed";
          break;
        case 'mouseup':
          elem.innerHTML += "...and released.";
          break;
      }
    }
  }

  let menu = new Menu();
  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

handleEvent 方法不必通过自身完成所有的工作。它可以调用其他特定于事件的方法,例如:

<button id="elem">Click me</button>

<script>
  class Menu {
    handleEvent(event) {
      // mousedown -> onMousedown
      let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
      this[method](event);
    }

    onMousedown() {
      elem.innerHTML = "Mouse button pressed";
    }

    onMouseup() {
      elem.innerHTML += "...and released.";
    }
  }

  let menu = new Menu();
  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

在这里,事件处理程序已经分离了出来,更容易进行代码编写和后续维护。

二、操作元素

1.改变元素内容

// 从起始位置到终止位置的内容,但会去除 HTML 标签(不识别标签),同时空格和换行也会去掉
element.innerText
// 起始到终止位置全部内容,包括 HTML 标签,保留空格和换行
element.innerHTML

2.修改元素属性

常用元素的属性操作

1.innerText、innerHTML 改变元素内容

2.src、href

3.id、alt、title

3.修改表单属性

利用 DOM 可以操作如下表单元素的属性:

type、value、checked、selected、disabled

4.修改样式属性

1.element.style 行内样式操作

2.element.className 类名样式操作

  • 若样式修改较多,可以采取操作类名方式更改元素样式
  • className 会直接更改元素类名,会覆盖原先的类名class.className = ‘first second’; 这样会显示 second 的样式,且继承了 first 的样式。
显示隐藏文本框内容
<script>
    // 获取元素
    var text = document.querySelector('input');
    // 注册事件,获得焦点事件 onfocus
    text.onfucus = function() {
        if (this.value === '手机') {
            this.value = '';
        }
        // 把文本框例的文字颜色变黑
        this.style.color = "#333";
    }
    // 注册事件 失去焦点事件 onblur
    text.onblur = function() {
        if(this.value === '') {
            this.value = '手机';
        }
        //失去焦点需要把文本框里面的文字颜色变浅色
        this.style.color = '#999';
    }
</script>

5.排他思想(算法)

如果有一组元素,若想要某一个元素实现某种样式,则需要用到循环的排他思想算法:

1.清除所有元素样式

2.给当前元素设置样式

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <script>
        // 获取所有按钮
        var btns = document.getElementsByTagName('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                // 清除样式
                for (var i = 0; i < btns.length; i++) {
                    btns[i].style.backgroundColor = '';
                }
                // 设置样式
                this.style.backgroundColor = 'pink';
        }
    </script>
</body>

6.自定义属性

自定义属性目的:为了保存数据到页面中而不用保存到数据库中。

有些自定义属性很容易引起歧义,不易判断是否为自定义。

1.获取属性值
  • element.属性 // 获取内置属性值(元素本身自带的属性)
  • element.getAttribute(‘属性’); // 获得自定义的属性H5 新增:
  • element.dataset.index 或者 element.dataset['index']
2.设置属性值
  • element.属性 = ‘值’ // 设置内置属性值
  • element.setAttribute(‘属性’, ‘值’); // 针对自定义属性
3.移除属性
  • element.removeAttribute('属性');
4.设置H5 自定义属性

H5规定自定义属性要 data- 开头作为属性名并且赋值。

例:<div data-index="1">l</div>

或使用 JS 设置 element.setAttribute("data-index", 2);

三、事件高级

1.注册事件

传统注册方式
  • 利用 on 开头的事件 onclick
  • <button onclick = "alert("hi")"></button>
  • btn.onclick = function() {}
  • 特点: 注册事件的 唯一性
  • 同一元素同一事件只能设置一个处理函数,最后注册的处理函数会覆盖前面注册的处理函数
方法监听注册方式
  • addEventListener() 它是一个方法
  • IE9 之前不支持,可用 attachEvent() 代替
  • 特点:同一元素同一事件可用注册多个监听器
  • 按注册顺序依次执行
addEventListener () 事件监听方式
eventTarget.addEventListener(type, listener[, useCapture])
btn.addEventListener("click", function() {
    alert("hi");
})

eventTarget.addEventListener() 方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定事件时,会执行事件处理函数。

  • type: 事件类型字符串,比如: click、mouseover 注意不要带 on 且要加引号
  • listener: 事件处理函数,事件发生时,会调用该监听函数
  • useCapture: 可选参数,是一个布尔值,默认是 false

cookie 存储

一、cookie 简介

cookie 是当用户浏览网站时,网站存储在用户计算机上的一个文本文件,记录了用户的 ID、密码、浏览过的网页、停留的事件等信息,网站可以通过读取 cookit 得知用户信息,避免每次访问都登录,或者做出相应的动作。

二、cookie 分类

cookie 分为 临时 cookie (会话 cookie) 和 持久 cookie。

  • 会话 cookie 指不设定它的生命周期,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话 cookie 就会销毁。
  • 持久 cookie 则设定了它的生命周期,关闭浏览器后不会销毁,直到设定的过期时间到期,浏览器会把 cookie 保存在硬盘上,可以在同一个浏览器中传递数据,使用户保持登录状态。

三、cookie 的创建、查找、修改和删除

1.创建 cookie

<script>
    document.cookie="username = name";
</script>

在页面中保存变量 name 的值到 cookie 中。

2.查找 cookie

<script>
    document.cookie="username =  jack"
    var x = document.cookie;
    document.write(x);
</script>

先创建了一个 cookie ,赋值给变量 x ,然后打印到页面中。

3.修改 cookie

<script>
    document.cookie="username=jack";
    document.cookie="username=mark";	// 新建的 cookie
    var x = document.cookie;
    document.write(x);
</script>

cookie 的修改类似于创建,旧的 cookie 将被覆盖。

4.删除 cookie

<script>
    document.cookie="username=jack";
    document.cookie="username=mark;expires=Thu, 01 Jan 1970 00:00:00 GMT";
    var x = document.cookie;
    document.write(x);
</script>

删除 cookie 只需要将有效期设置为以前的时间即可。

5. cookie 属性
<script>
    document.cookie="username=name;path=/";
</script>
属性名 含义 例子
path 读取 cookie 的路径 path=/;
expires cookie 的过期时间 expires=Thu 01 Jan 1970 00:00:00 GMT;
domain 读取 cookie 的域名 domain=taobao.com;
secure 传输加密 Secure;
1.path

默认情况下,与创建 cookie 的网页在同一目录或子目录下的所有网页都可以读取 cookie。

path 中区分大小写,若 path=/D:/one 写成 path=/d:/one ,那么就会形成一个新的 cookie。

<script>
    // 设置多个值
    document.cookie="name=jack,sex=men,age=18";
</script>
2.expires

expires 用来控制 cookie 的生命周期,时间格式必须是标准的 GMT 格式,通常 cookie 时间都采用在当前时间的基础上增加指定时间的方式:

<script>
    var expire = new Date(new Date().getTime() + 1*600000);	//相对于当前,设置了10分钟的 cookie 有效期
</script>
3.domain

一般网站除了主域名以外,还有很多二级域名,通常处于安全考虑,只允许每个域名访问自己所创建的 cookie ,如果想要多个域共享 cookie 信息,就需要设置 cookie 的 domain 属性。

<script>
    document.cookie = "name=jack;domain=baidu.com"	// cookie 被百度所有二级域中的页面共享
</script>
4.secure

secure 属性是用来保证 cookie 安全的,表示创建的 cookie 只能在 HTTPS 链接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息。

<script>
    document.cookie="name=jack,sex=men,age=18;domain=baidu.com;secure"
</script>
5.cookie 案例
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>cookie</title>
</head>
<head>
	<script>
        // 设置 cookie 值的函数,创建一个函数用于存储访问者的名字
        function setCookie(username,name) {
            var d = new Date();
            d.setTime(d.getTime() + 60000);	// 设置 cookie 有效时间, cookie 一分钟后到期
            // 根据格林威治时间(GMT) 把 Date 对象转换为字符串并返回结果
            var expires = "expires=" + d.toGMTString();
            document.cookie=username + "=" + name +o ";" + expires;
        }
        
        // 获取 cookie 值的函数
        function getCookie(username) {
            var value=username + "=";	// 创建一个用于检索指定 cookie:username+"=" 的变量 value
            var arr = document.cookie.split(';');
            for(var i=0; i<arr.length; i++) {
                var a = arr[i];
                if (a.indexOf(value) == 0) {	// 找到 cookie 值
                    return a.substr(value.length);	// 返回 cookie 值
                }
            }
            return "";	// 若没有找到 cookie,返回 ""
        }
        
        // 检查是否有 cookie 值的函数
        function checkCookie() {
            var user = getCookie("users");	// 调用 getCookie 函数,传入参数,获取 cookie 值 user
            // 如果 user 不为空
            if(user != ""){
                alert(user + "你好欢迎再次访问");
            }
            else {
                uesr = prompt("请输入你的姓名:");	// 弹窗
                if (user != "" && user != null){
                    setCookie("users",user);	// 调用函数,传参,创建 cookie
                }
            }
        }
    </script>    
</head>
<body onload="checkCookie()"></body>
</html>
正文完
 0
Yichend
版权声明:本站原创文章,由 Yichend 于2022-09-24发表,共计41364字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)