js.md

script 元素

  • async: 会立即下载次脚本,不会妨碍阻塞其他操作,比如下载其他资源和加载其他脚本仅对外部文件有效

  • defer: 表示 脚本可以延迟到文档完全被解析和显示之后再执行;仅对外部脚本有效,兼容性好


基本概念

typeof

  var message = "some string";
alert(typeof message); // "string"
alert(typeof(message)); // "string"
alert(typeof 95); // "number" 
  • typeof 操作符 是一个 操作符 不是函数;

null

null值 表示一个空对象指针所以使用typeof操作符检测时会返回“object”

  var car = null;
    alert(typeof car); // "object" 
    if (car != null){
    // 
    } 
    alert(null == undefined); //true

如果定义的变量将来用来保存对象,最好将该变量初始化为null而不是其他只;只要检测null值就额可以知道相应的变量是否已经保存了一个对象的引用if (car != null)。

无论什么情况下都没必要将一个变量显示地设置为undefined.


Number类型

  if (a + b == 0.3){ // 不要做这样的测试, 因此, 永远不要测试某个特点的浮点数值。
        alert("You got 0.3.");
    } 

数值转换

有三个函数可以把非数值转换为数值:Number(). parseInt(), parseFloat()

  • Number() 可以用于任何数据类型;parseInt(), parseFloat()则专门用于把字符串转换成数值。

  • Number().parseInt() 具有第二个参数,表示进制,parseFloat()只解析十进制因此没有第二个参数

  • Number:

    • 布尔值ture false 返回 1、0
    • null/"" => 0
    • undefined => NaN
    • 字符串:
      var num1 = Number("Hello world!"); //NaN
      var num2 = Number(""); //0
      var num3 = Number("000011"); //11
      var num4 = Number(true); //1 
      

    Number转换各种数据类型确实有点复杂且不够合理,因此在处理整数的时候更常用parseInt()

  • parseInt(): 转换时,他会 忽略字符串前面的空格,如果第一个字符不是数字或者负号,就会返回NaN。


  • toString() / String()

  • 数值。布尔。对象和字符串都有toString()方法,null/ undefined 没有这个方法
    toString()里面可以传 基数,表示进制

  • 在不知是null,undefined。下 可以用String(),他能够将任何类型的值转换为字符串,遵循(如果有toString()则调,如果是null,undefined则返回"null,undefined")

  • 值转字符串也可以用 + ""

 var value1 = 10;
    var value2 = true;
    var value3 = null;
    var value4;
    alert(String(value1)); // "10"
    alert(String(value2)); // "true"
    alert(String(value3)); // "null"
    alert(String(value4)); // "undefined"

一元加和减操作符

var num = 25;
num = +num; // 25

  • 一元加 +放在数值前面不会对数值产生任何影响;在对非数值时,则会像Number()一样进行转换。
   var s1 = "01";
   var s2 = "1.1";
   var s3 = "z";
   var b = false;
   var f = 1.1;
   var o = {
   valueOf: function() {
   return -1;
   }
   };
   s1 = +s1; //  1
   s2 = +s2; // 1.1
   s3 = +s3; //  NaN
   b = +b; //  0
   f = +f; //  1.1
   o = +o; // -1 
  • 一元减
    var num = 25;
    num = -num; // -25
  • 在对数值时会将其变为负数,在对非数值时规则同一元加最后再得到的数值转换为负数。

***传递参数 ***
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数
中,就像从一个变量复制到另一个变量一样。

***变量声明 ***

  1. 使用 var 的函数作用域声明
    在使用 var 声明变量时,变量会被自动添加到最接近的上下文。在函数中,最接近的上下文就是函
    数的局部上下文。在 with 语句中,最接近的上下文也是函数上下文。如果变量未经声明就被初始化了,
    那么它就会自动被添加到全局上下文,
Date
比如,要创建一个表示“2019 年 5 月 23 日”的日期对象,可以使用以下代码:

let someDate = new Date(Date.parse(“May 23, 2019”));
如果传给 Date.parse()的字符串并不表示日期,则该方法会返回 NaN。如果直接把表示日期的字
符串传给 Date 构造函数,那么 Date 会在后台调用 Date.parse()。换句话说,下面这行代码跟前面
那行代码是等价的:
let someDate = new Date(“May 23, 2019”);
这两行代码得到的日期对象相同。

ECMAScript 还提供了 Date.now()方法,返回表示方法执行时日期和时间的毫秒数。

RegExp

g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。
i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。
m:多行模式,表示查找到一行文本末尾时会继续查找。
y:粘附模式,表示只查找从 lastIndex 开始及之后的字符串。
u:Unicode 模式,启用 Unicode 匹配。
s:dotAll 模式,表示元字符.匹配任何字符(包括\n 或\r)。

// 匹配字符串中的所有"at"
let pattern1 = /at/g;
// 匹配第一个"bat"或"cat",忽略大小写
let pattern2 = /[bc]at/i;
// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;

所有元字符在模式中也必须转义,包括:( [ { \ ^ $ | ) ] } ? * + .

slice()、substr()和 substring()

ECMAScript 提供了 3 个从字符串中提取子字符串的方法:slice()、substr()和 substring()。这
3个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数。第一个参数表示子字符串开
始的位置,第二个参数表示子字符串结束的位置。对 slice()和 substring()而言,第二个参数是提取结
束的位置(即该位置之前的字符会被提取出来)。对 substr()而言,第二个参数表示返回的子字符串数量。
任何情况下,省略第二个参数都意味着提取到字符串末尾。与 concat()方法一样,slice()、substr()
和 substring()也不会修改调用它们的字符串,而只会返回提取到的原始新字符串值
let stringValue = “hello world”;
console.log(stringValue.slice(3)); // “lo world”
console.log(stringValue.substring(3)); // “lo world”
console.log(stringValue.substr(3)); // “lo world”
console.log(stringValue.slice(3, 7)); // “lo w”
console.log(stringValue.substring(3,7)); // “lo w”
console.log(stringValue.substr(3, 7)); // “lo worl”

trim()方法

ECMAScript 在所有字符串上都提供了 trim()方法。这个方法会创建字符串的一个副本,删除前、
后所有空格符,再返回结果。比如:
let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();
console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // “hello world”
由于 trim()返回的是字符串的副本,因此原始字符串不受影响,即原本的前、后空格符都会保留。
另外,trimLeft()和 trimRight()方法分别用于从字符串开始和末尾清理空格符。

repeat()方法

ECMAScript 在所有字符串上都提供了 repeat()方法。这个方法接收一个整数参数,表示要将字
符串复制多少次,然后返回拼接所有副本后的结果。
let stringValue = "na ";
console.log(stringValue.repeat(16) + “batman”);
// na na na na na na na na na na na na na na na na batman

toLocaleLowerCase()和 toLocaleUpperCase()方法旨在基于
特定地区实现。在很多地区,地区特定的方法与通用的方法是一样的。但在少数语言中(如土耳其语),
Unicode 大小写转换需应用特殊规则,要使用地区特定的方法才能实现正确转换。

let stringValue = “hello world”;
console.log(stringValue.toLocaleUpperCase()); // “HELLO WORLD”
console.log(stringValue.toUpperCase()); // “HELLO WORLD”
console.log(stringValue.toLocaleLowerCase()); // “hello world”
console.log(stringValue.toLowerCase()); // “hello world”
如果不知道代码涉及什么语言,则最好使用地区特定的转换方法。

URL 编码方法

这两个方法的主要区别是,encodeURI()不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、
井号,而 encodeURIComponent()会编码它发现的所有非标准字符。来看下面的例子:
let uri = “http://www.wrox.com/illegal value.js#start”;
// “http://www.wrox.com/illegal%20value.js#start”
console.log(encodeURI(uri));
// “http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start”
console.log(encodeURIComponent(uri));

这里使用 encodeURI()编码后,除空格被替换为%20 之外,没有任何变化。而 encodeURIComponent()方法将所有非字母字符都替换成了相应的编码形式。这就是使用 encodeURI()编码整个
URI,但只使用 encodeURIComponent()编码那些会追加到已有 URI 后面的字符串的原因。

Math

let max = Math.max(1,2,4) // 4
let max = Math.min(1,2,4) // 1

let values = [1, 2, 3, 4, 5, 6, 7, 8];
let max = Math.max(…values)

function selectFrom(lowerValue, upperValue) {
let choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
let num = selectFrom(2,10);
console.log(num); // 2~10 范围内的值,其中包含 2 和 10

try/catch 语句

即使在 catch 块中不使用错误对象,也必须为它定义名称。错误对象中暴露的实际信息因浏览器而异,但至少包含保存错误消息的 message
属性。
try {
window.someFunction();
} catch (error){
console.log(error.message);
}
message 属性是唯一一个在 IE、Firefox、Safari、
Chrome 和 Opera 中都有的属性,

  1. finally 子句

try 或 catch 块无法阻止 finally 块执行,包括 return 语句。

  1. 错误类型

ECMA-262 定义了以下 8 种错误类型:
Error
InternalError
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError

Error 是基类型,其他错误类型继承该类型。因此,所有错误类型都共享相同的属性(所有错误对象上的方法都是这个默认类型定义的方法)。浏览器很少会抛出 Error 类型的错误,该类型主要用于开发者抛出自定义错误。

InternalError 例如,递归过多导致了栈溢出。这个类型并不是代码中通常要处理的错误

EvalError 类型的错误会在使用 eval()函数发生异常时抛出。几乎不出现。

RangeError 错误会在数值越界时抛出。例如,定义数组时如果设置了并不支持的长度。

ReferenceError 会在找不到对象时发生。(这就是著名的"object expected"浏览器错误的原因。)这种错误经常是由访问不存在的变量而导致的,比如:
let obj = x; // 在 x 没有声明时会抛出 ReferenceError

SyntaxError 经常在给 eval()传入的字符串包含 JavaScript 语法错误时发生

TypeError 在 JavaScript 中很常见,主要发生在变量不是预期类型,或者访问不存在的方法时。很
多原因可能导致这种错误,尤其是在使用类型特定的操作而变量类型不对时。

URIError,只会在使用 encodeURI()或 decodeURI()但传入了格式错误的URI 时发生。
这个错误恐怕是 JavaScript 中难得一见的错误了,因为上面这两个函数非常稳健。

在 try/catch 语句
的 catch 块中,可以使用 instanceof 操作符确定错误的类型,比如:
try {
someFunction();
} catch (error){
if (error instanceof TypeError){
// 处理类型错误
} else if (error instanceof ReferenceError){
// 处理引用错误
} else {
// 处理所有其他类型的错误
}
}

自定义错误类型,需要提供 name , message 属性

class customError extends Error{
constructor(message){
super(message)
this.name = name;
this.message = message;
}
}
throw new CustomError(“my message”);

把错误记录到服务器中

function logError(sev, msg) {
let img = new Image(),
encodedSev = encodeURIComponent(sev),
encodedMsg = encodeURIComponent(msg);
img.src = ‘log.php?sev=KaTeX parse error: Expected 'EOF', got '&' at position 13: {encodedSev}&̲msg={encodedMsg}’;
}
logError()函数接收两个参数:严重程度和错误消息。严重程度可以是数值或字符串,具体取决
于使用的日志系统。这里使用 Image 对象发送请求主要是从灵活性方面考虑的。所有浏览器都支持 Image 对象,
即使不支持 XMLHttpRequest 对象也一样。不受跨域规则限制。通常,接收错误消息的应该是多个服务器中的一个,而 XMLHttpRequest
此时就比较麻烦。
记录错误的过程很少出错。大多数 Ajax 通信借助 JavaScript 库的包装来处理。如果这个库本身
出错,而你又要利用它记录错误,那么显然错误消息永远不会发给服务器。
只要是使用 try/catch 语句的地方,都可以把相关错误记录下来

####抛出错误

function divide(num1, num2) {
if (typeof num1 != “number” || typeof num2 != “number”){
throw new Error(“divide(): Both arguments must be numbers.”);
}
return num1 / num2;
}

在大型应用程序中,自定义错误通常使用 assert()函数抛出错误。这个函数接收一个应该为 true
的条件,并在条件为 false 时抛出错误。下面是一个基本的 assert()函数:
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}

function divide(num1, num2) {
assert(typeof num1 == “number” && typeof num2 == “number”,
“divide(): Both arguments must be numbers.”);
return num1 / num2;
}

客户端存储
  1. cookie
    cookie 是与特定域绑定的。设置 cookie 后,它会与请求一起发送到创建它的域。这个限制能保证
    cookie 中存储的信息只对被认可的接收者开放,不被其他域访问。
集合引用类型
  1. object

console.log(person[“name”]); // “Nicholas”
console.log(person.name); // “Nicholas”
从功能上讲,这两种存取属性的方式没有区别。使用中括号的主要优势就是可以通过变量访问属性
let propertyName = “name”;
console.log(person[propertyName]); // “Nicholas”

  1. Array

Array 构造函数还有两个 ES6 新增的用于创建数组的静态方法:from()和 of()。from()用于将
类数组结构转换为数组实例,而 of()用于将一组参数转换为数组实例。

// 字符串会被拆分为单字符数组
console.log(Array.from(“Matt”)); // [“M”, “a”, “t”, “t”]

// Array.from()对现有数组执行浅复制
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
console.log(a1); // [1, 2, 3, 4]
alert(a1 === a2); // false

// from()也能转换带有必要属性的自定义对象
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4
};
console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4]

const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, x => x**2);//[1, 4, 9, 16]

Array.of()可以把一组参数转换为数组。这个方法用于替代在 ES6之前常用的 Array.prototype.slice.call(arguments),一种异常笨拙的将 arguments 对象转换为数组的写法:
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(undefined)); // [undefined]

数组索引

数组 length 属性的独特之处在于,它不是只读的。通过修改 length 属性,可以从数组末尾删除或添加元素。

let colors = [“red”, “blue”, “green”]; // 创建一个包含 3 个字符串的数组
colors.length = 2;
alert(colors[2]); // undefined

let colors = [“red”, “blue”, “green”]; // 创建一个包含 3 个字符串的数组
colors.length = 4;
alert(colors[3]); // undefined

let colors = [“red”, “blue”, “green”]; // 创建一个包含 3 个字符串的数组
colors[colors.length] = “black”; // 添加一种颜色(位置 3)
colors[colors.length] = “brown”; // 再添加一种颜色(位置 4)

检测数组

是判断一个对象是不是数组。在只有一个网页(因而只有一个全局作
用域)的情况下,使用 instanceof 操作符就足矣:
if (value instanceof Array){
// 操作数组
}
使用 instanceof 的问题是假定只有一个全局执行上下文.
如果网页里有多个框架,则可能涉及两
个不同的全局执行上下文,因此就会有两个不同版本的 Array 构造函数

为解决这个问题ECMAScript 提供了Array.isArray()方法。这个方法的目的就是确定一个值是
否为数组,而不用管它是在哪个全局执行上下文中创建的。
if (Array.isArray(value)){
// 操作数组
}

迭代器方法

在 ES6 中,Array 的原型上暴露了 3 个用于检索数组内容的方法:keys()、values()和
entries()。keys()返回数组索引的迭代器,values()返回数组元素的迭代器,而 entries()返回
索引/值对的迭代器

const a = [“foo”, “bar”, “baz”, “qux”];
// 因为这些方法都返回迭代器,所以可以将它们的内容
// 通过 Array.from()直接转换为数组实例
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries());
console.log(aKeys); // [0, 1, 2, 3]
console.log(aValues); // [“foo”, “bar”, “baz”, “qux”]
console.log(aEntries); // [[0, “foo”], [1, “bar”], [2, “baz”], [3, “qux”]]

搜索和位置方法

find()和 findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回
第一个匹配的元素,findIndex()返回第一个匹配元素的索引。这两个方法也都接收第二个可选的参数,
用于指定断言函数内部 this 的值。
const people = [
{
name: “Matt”,
age: 27
},
{
name: “Nicholas”,
age: 29
}
];
alert(people.find((element, index, array) => element.age < 28));
// {name: “Matt”, age: 27}
alert(people.findIndex((element, index, array) => element.age < 28));
// 0

迭代方法

every():对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true。
filter():对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。
forEach():对数组每一项都运行传入的函数,没有返回值。
map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
some():对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。
这些方法都不改变调用它们的数组。

####Map

作为 ECMAScript 6 的新增特性,Map 是一种新的集合类型,为这门语言带来了真正的键/值存储机
制.

初始化之后,可以使用 set()方法再添加键/值对。另外,可以使用 get()和 has()进行查询,可
以通过 size 属性获取映射中的键/值对的数量,还可以使用 delete()和 clear()删除值。

const m = new Map();
alert(m.has(“firstName”)); // false
alert(m.get(“firstName”)); // undefined
alert(m.size);

set()方法返回映射实例,因此可以把多个操作连缀起来,

与 Object 只能使用数值、字符串或符号作为键不同,Map 可以使用任何 JavaScript 数据类型作为
键。Map 内部使用 SameValueZero 比较操作(ECMAScript 规范内部定义,语言中不能使用),基本上相
当于使用严格对象相等的标准来检查键的匹配性。与 Object 类似,映射的值是没有限制的。

const m = new Map();
const functionKey = function() {};
const symbolKey = Symbol();
const objectKey = new Object();
m.set(functionKey, “functionValue”);
m.set(symbolKey, “symbolValue”);
m.set(objectKey, “objectValue”);
alert(m.get(functionKey)); // functionValue
alert(m.get(symbolKey)); // symbolValue
alert(m.get(objectKey)); // objectValue

对象 object

Object.defineProperty()

读取属性的特性:使用 Object.getOwnPropertyDescriptor()方法可以取得指定属性的属性描述符。

ECMAScript 2017 新增了 Object.getOwnPropertyDescriptors()静态方法。这个方法实际上
会在每个自有属性上调用 Object.getOwnPropertyDescriptor()并在一个新对象中返回它们。

合并对象

ECMAScript 6 专门为合并对象提供了 Object.assign()方法。是浅复制;如果多个源对象都有相同的属性,则使
用最后一个复制的值。

如果赋值期间出错,则操作会中止并退出,同时抛出错误。Object.assign()没有“回滚”之前
赋值的概念,因此它是一个尽力而为、可能只会完成部分复制的方法。

let dest, src, result;
/**

  • 错误处理
    */
    dest = {};
    src = {
    a: ‘foo’,
    get b() {
    // Object.assign()在调用这个获取函数时会抛出错误
    throw new Error();
    },
    c: ‘bar’
    };
    try {
    Object.assign(dest, src);
    } catch(e) {}
    // Object.assign()没办法回滚已经完成的修改
    // 因此在抛出错误之前,目标对象上已经完成的修改会继续存在:
    console.log(dest); // { a: foo }

#####对象标识及相等判定

// 这些是===符合预期的情况
console.log(true === 1); // false
console.log({} === {}); // false
console.log(“2” === 2); // false
// 这些情况在不同 JavaScript 引擎中表现不同,但仍被认为相等
console.log(+0 === -0); // true
console.log(+0 === 0); // true
console.log(-0 === 0); // true
// 要确定 NaN 的相等性,必须使用极为讨厌的 isNaN()
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true

为改善这类情况,ECMAScript 6 规范新增了 Object.is(),这个方法与===很像,但同时也考虑
到了上述边界情形。这个方法必须接收两个参数

console.log(Object.is(true, 1)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is(“2”, 2)); // false
// 正确的 0、-0、+0 相等/不等判定
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
console.log(Object.is(-0, 0)); // false
// 正确的 NaN 相等判定
console.log(Object.is(NaN, NaN)); // true
要检查超过两个值,递归地利用相等性传递即可:
function recursivelyCheckEqual(x, …rest) {
return Object.is(x, rest[0]) &&
(rest.length < 2 || recursivelyCheckEqual(…rest));
}

增强的对象语法

old:
const jobKey = ‘job’;
let person = {};
person[jobKey] = ‘Software engineer’;

new es6 有了可计算属性

const jobKey = ‘job’;
let person = {
[jobKey]: ‘Software engineer’
};

因为被当作 JavaScript 表达式求值所以可计算属性本身可以是复杂的表达式,在实例化时再求值
const nameKey = ‘name’;
const ageKey = ‘age’;
const jobKey = ‘job’;
let uniqueToken = 0;
function getUniqueKey(key) {
return ${key}_${uniqueToken++};
}
let person = {
[getUniqueKey(nameKey)]: ‘Matt’,
[getUniqueKey(ageKey)]: 27,
[getUniqueKey(jobKey)]: ‘Software engineer’
};

######简写方法名

let person = {
sayName: function(name) {
console.log(My name is ${name});
}
};

let person = {
sayName(name) {
console.log(My name is ${name});
}
};

简写方法名与可计算属性键相互兼容:
const methodKey = ‘sayName’;
let person = {
methodKey {
console.log(My name is ${name});
}
}

####对象解构

对象解构就是使用与对象匹配的结构来实现对象属性赋值
// 使用对象解构
let person = {
name: ‘Matt’,
age: 27
};
let { name: personName, age: personAge } = person;
console.log(personName); // Matt
console.log(personAge); // 27
简写: let { name, age } = person;

而如果引用的属性不存在,则该变量的值就是 undefined:
let { name, job } = person; console.log(job); // undefined

也可以在解构赋值的同时定义默认值let { name, job=‘Software engineer’ } = person;
console.log(job); // Software engineer

解构在内部使用函数 ToObject()(不能在运行时环境中直接访问)把源数据结构转换为对象。这
意味着在对象解构的上下文中,原始值会被当成对象。这也意味着(根据 ToObject()的定义),null
和 undefined 不能被解构否则会抛出错误

let { length } = ‘foobar’;
console.log(length); // 6
let { _ } = null; // TypeError
let { _ } = undefined; // TypeError

解构赋值可以使用嵌套结构,以匹配嵌套的属性:

let person = {
name: ‘Matt’,
age: 27,
job: {
title: ‘Software engineer’
}
};
// 声明 title 变量并将 person.job.title 的值赋给它
let { job: { title } } = person;
console.log(title); // Software engineer

在外层属性没有定义的情况下不能使用嵌套解构。无论源对象还是目标对象都一样。

部分解构
如果一个解构表达式涉及多个赋值,开始的赋值成功而后面的赋值出错,则整个解构赋值只会完成一部分

参数上下文匹配

对参数的解构赋值不会影响 arguments 对象,但可以在函数签名中声明在函数体内使用局部变量:
let person = {
name: ‘Matt’,
age: 27
};

function printPerson(foo, {name, age}, bar) {
console.log(arguments);
console.log(name, age);
}
printPerson(‘1st’, person, ‘2nd’);
// [‘1st’, { name: ‘Matt’, age: 27 }, ‘2nd’]
// ‘Matt’, 27

创建对象

  1. 工厂模式
    function createPerson(name, age, job) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
    console.log(this.name);
    };
    return o;
    }
    let person1 = createPerson(“Nicholas”, 29, “Software Engineer”);

这种工厂模式虽
然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)。

  1. 构造函数模式:ECMAScript 中的构造函数是用于创建特定类型对象的

比如,前面的例子使用构造函数模式可以这样写:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
};
}
let person1 = new Person(“Nicholas”, 29, “Software Engineer”);

Person()内部
的代码跟 createPerson()基本是一样的,只是有如下区别。
没有显式地创建对象。
属性和方法直接赋值给了 this。
没有 return。
另外,要注意函数名 Person 的首字母大写了。

要创建 Person 的实例,应使用 new 操作符。以这种方式调用构造函数会执行如下操作:

  • 在内存中创建一个对象
  • 这个新对象的内部的 Prototype 特性被复制为构造函数的prototype属性。
  • 构造函数内部的this被赋值为这个新对象(即this指向新对象)
  • 执行构造函数内部的代码(给新对象添加属性)
  • 如果构造函数返回非空对象,则返回该对象,否则返回刚创建的新对象。

person1 和 person2 分别保存着 Person 的不同实例。这两个对象都有一个
constructor 属性指向 Person,如下所示:
console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // true

构造函数不一定要写成函数声明的形式。赋值给变量的函数表达式也可以表示构造函数:
let Person = function(name, age, job) {}

在实例化时,如果不想传参数,那么构造函数后面的括号可加可不加。只要有 new 操作符,就可以
调用相应的构造函数
let person1 = new Person();
let person2 = new Person;

任何函数只要使用 new 操作符调用就是构造函数,而不使用 new 操作符调用的函数就是普通函数

在调用一个函数而没有明确设置 this 值的情况下(即没有作为对象的方法调用,或
者没有使用 call()/apply()调用),this 始终指向 Global 对象(在浏览器中就是 window 对象)。

构造函数的问题:
构造函数虽然有用,但也不是没有问题。构造函数的主要问题在于,其定义的方法会在每个实例上
都创建一遍。

#####原型模式

每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例
共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处
是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以
直接赋值给它们的原型,
function Person() {}
Person.prototype.name = “Nicholas”;
Person.prototype.age = 29;
Person.prototype.job = “Software Engineer”;
Person.prototype.sayName = function() {
console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // “Nicholas”
let person2 = new Person();
person2.sayName(); // “Nicholas”
console.log(person1.sayName == person2.sayName); // true

类 class

// 类声明
class Person {}
// 类表达式
const Animal = class {};
与函数表达式类似,类表达式在它们被求值前也不能引用。不过,与函数定义不同的是,虽然函数
声明可以提升,但类定义不能
另一个跟函数声明不同的地方是,函数受函数作用域限制,而类受块作用域限制
//class
// new:调用类的构造函数
// 1. 在内存中创建一个对象
// 2. 这个新对象内部的属性指针被赋值为构造函数的prototype属性
// 3. 构造函数的内的this指向新对象
// 4. 执行构造函数内部的代码
// 5. 如果构造函数返回非空对象,则返回该对象,否则返回刚创建的对象
// 空类的定义
class Foo {}

// you 构造函的类
class Bar {
    constructor() {}
}
// 有获取函数的类
class BAsx {
    get myXT() {}
}
// 有静态方法
class QEX {
    static myEWR() {}
}
// 作用域
let Person = class PersonNAme {
    identify() {
        console.log(Person.name, PersonNAme.name);
    }
}
let p = new Person()
p.identify()
console.log(Person.name);
// console.log(PersonNAme.name); // xx is not definde

// 类构造函数
// constructor 用于在类模块内部创建类的构造函数,方法名字会告诉解释器在用new操作符的创建类的新实例的时候,应该调用这个函数,构造函数的定义不是必须的,不定义构造函数相当于讲构造函数定位为空函数
class Animal {}
class Persons {
    constructor() {
        console.log("EEXER");
    }
}
class Vaegtabl {
    constructor() {
        this.corlor = "FSDFDS"
    }
}
var a = new Animal()
var b = new Persons()
var c = new Vaegtabl()
console.log(c.corlor);
// 类实例话时候传入的参数会作用于构造函数的参数, 不传参数的话,类后面的括号可选
class Psrkfdsn {
    constructor(name) {
        console.log(arguments.length);
        this.name = name || null;
    }
}
var spr = new Psrkfdsn;
var fddsn = new Psrkfdsn()
var fddsndfsd = new Psrkfdsn("neam")
// 构造函数和类构造函数的区别是 调用类构造函数必须用new操作符,而普通构造函数不需要new,那么就会以全局的this(通常是window)作为内部对象。调用类构造函数如忘记new 则跑出错误。
function Prwesdfs() {}
class Anmeifd {}
// 吧window作为this。来构建实例
var dfsdk = Prwesdfs()

// 静态方法,每个类上只能有一个
class Rgfdsg {
    constructor() {
        this.locate = () => {
            console.log("AAAAAAa", this);
        }
    }
    locate() {
        console.log("prototype", this);
    }
    static locate() {
        console.log("STatic", this);
    }
}
var dofd = new Rgfdsg()
dofd.locate()
Rgfdsg.prototype.locate();
Rgfdsg.locate()
// 静态方法非常适合做实例工厂
class Dhkms {
    constructor(age) {
        this.age_ = age,
            this.name_ = name
    }
    sayAge() {
        console.log(this.age_);
    }
    static create() {
        return new Dhkms(Math.floor(Math.random() * 100))
    }
}
console.log(Dhkms.create())

// 继承
// extends 关键字可以继承任何拥有,construct 和 原型的对象.

// 派生类都会通过原型链访问到类和原型上的方法,this的值会反应电泳相应方法的实例和类:

class Vehdfs {
    identiiifddRorerpe(id) {
        console.log(id, this);
    }
    static indefdkClass(id) {
        console.log(id, this);
    }
}
class Bus extends Vehdfs {}
var fuds = new Vehdfs()
var fngssd = new Bus()

fuds.identiiifddRorerpe("ver")
fngssd.identiiifddRorerpe("Bus")

Bus.indefdkClass("BS")
Vehdfs.indefdkClass("Ve")

// 派生类的方法可以通过调用super关键字引用他们的原型,super只能在派生类中使用,且限制于类构造函数和实例方法和静态方法内部,在类构造函数中调用可以访问父类的构造函数
class Vae {
    constructor() {
        this.hasEngine = true;
    }
}

class Busvae extends Vae {
    constructor() {
        // 不要在调用super ()之前调用this,否则跑出ReferenceError错误
        super() // 相当于 super.constructor
        console.log(this instanceof Vae);
        console.log(this);
    }
}
new Busvae()

// 在静态方法中可以通过 super 调用继承的类上定义的静态方法:
class Efdssfs {
    static identify() {
        console.log("VE");
    }
}
class Bise extends Efdssfs {
    static identify() {
        super.identify()
    }
}
Bise.identify()
// super 只能在派生类(extends 继承时)构造函数和静态方法中使用。 
// 不能单独引用 super 关键字,要么用它调用构造函数super(),要么用它引用静态方法super.xx。
// 调用 super()会调用父类构造函数,并将返回的实例赋值给 this。
// super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入。
// 如果没有定义类构造函数,在实例化派生类时会调用 super(),而且会传入所有传给派生类的参数。隐式自动调用

// 在类构造函数中,不能在调用 super()之前引用 this。
// class Vehicless {}
// class Bussss extends Vehicless {
//     constructor() {
//         console.log(this);
//     }
// }
// new Bussss();
// ReferenceError: Must call super constructor in derived class
// before accessing 'this' or returning from derived constructor 


// 如果在派生类中显式定义了构造函数,则要么必须在其中调用 super(),要么必须在其中返回
// 一个对象。
// class Vehicle {}
// class Car extends Vehicle {}
// class Bus extends Vehicle {
// constructor() {
// super();
// }
// }
// class Van extends Vehicle {
// constructor() {
// return {};
// }
// }
// console.log(new Car()); // Car {}
// console.log(new Bus()); // Bus {}
// console.log(new Van()); // {} 

// 继承 内置类型
class SuperArray extends Array {
    shuffle() {
        for (let i = this.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [this[i], this[j]] = [this[j], this[i]];
        }
    }
}
var xipai = new SuperArray(1, 2, 3, 4, 5)
console.log("xipai", xipai);
xipai.shuffle()
console.log(xipai);
DOM

HTML 中的每段标记都可以表示为这个树形结构中的一个节点。元素节点表示 HTML 元素,属性
节点表示属性,文档类型节点表示文档类型,注释节点表示注释。DOM 中总共有 12 种节点类型,这些
类型都继承一种基本类型。
每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值
常量表示

Node.ELEMENT_NODE(1)
Node.ATTRIBUTE_NODE(2)
Node.TEXT_NODE(3)
Node.CDATA_SECTION_NODE(4)
Node.ENTITY_REFERENCE_NODE(5)
Node.ENTITY_NODE(6)
Node.PROCESSING_INSTRUCTION_NODE(7)
Node.COMMENT_NODE(8)
Node.DOCUMENT_NODE(9)
Node.DOCUMENT_TYPE_NODE(10)
Node.DOCUMENT_FRAGMENT_NODE(11)
Node.NOTATION_NODE(12)
if (someNode.nodeType == Node.ELEMENT_NODE){
alert(“Node is an element.”);
}
这个例子比较了 someNode.nodeType 与 Node.ELEMENT_NODE 常量。如果两者相等,则意味着
someNode 是一个元素节点。
浏览器并不支持所有节点类型。开发者最常用到的是元素节点和文本节点。

  1. 节点关系
    每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组
    对象,,NodeList 并不是 Array 的实例,但可以使用中括
    号访问它的值,而且它也有 length 属性。NodeList 对象独特的地方在于,它其实是一个对 DOM 结
    构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。我们通常说 NodeList 是实时的
    活动对象,而不是第一次访问时所获得内容的快照

let firstChild = someNode.childNodes[0];
let secondChild = someNode.childNodes.item(1);
let count = someNode.childNodes.length;

使用 Array.prototype.slice()可以像前面介绍 arguments 时一样把 NodeList 对象转换为数组
let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
使用 ES6 的 Array.from()静态方法,可以替换这种笨拙的方式:
let arrayOfNodes = Array.from(someNode.childNodes);

用 previousSibling 和 nextSibling 可以在这个列
表的节点间导航。这个列表中第一个节点的 previousSibling 属性是 null,最后一个节点的
nextSibling 属性也是 null,

:firstChild 和 lastChild 分别指向
childNodes 中的第一个和最后一个子节点

someNode.lastChild 的值始终等于 someNode.childNodes[someNode.
childNodes.length-1]。

这是因为利用这些关系
指针,几乎可以访问到文档树中的任何节点,而这种便利性是 childNodes 的最大亮点。还有一个便利
的方法是 hasChildNodes(),这个方法如果返回 true 则说明节点有一个或多个子节点。相比查询
childNodes 的 length 属性,这个方法无疑更方便。

  1. 操纵节点
    DOM 又提供了一些操纵节点的方法。最常用的方法是appendChild(),用于在 childNodes 列表末尾添加节点

appendChild()方法返回新添加的节点,
let returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); // true
alert(someNode.lastChild == newNode); // true

如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。

如果想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用 insertBefore()方法。
这个方法接收两个参数:要插入的节点和参照节点。调用这个方法后,要插入的节点会变成参照节点的
前一个同胞节点,并被返回。如果参照节点是 null,则 insertBefore()与 appendChild()效果相
同,

appendChild() 和 insertBefore() 在插入节点时不会删除任何已有节点。相对地,
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档
树中完全移除,要插入的节点会取而代之。
与 replaceChild()方法一样,通过 removeChild()被移除的节点从技术上说仍然被同一个文档
所拥有,但文档中已经没有它的位置。
上面介绍的 4 个方法都用于操纵某个节点的子元素,也就是说使用它们之前必须先取得父节点(使
用前面介绍的 parentNode 属性)。并非所有节点类型都有子节点,如果在不支持子节点的节点上调用
这些方法,则会导致抛出错误。

所有节点类型还共享了两个方法。第一个是 cloneNode(),会返回与调用它的节点一模一样的节
点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制,
即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。

let deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); // 3(IE9 之前的版本)或 7(其他浏览器)
let shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); // 0

getElementsByTagName:是另一个常用来获取元素引用的方法这个方法返回一个
HTMLCollection 对象

window.location.href和document.location.href、document.URL的区别

1、document表示的是一个文档对象,window表示的是一个窗口对象,一个窗口下可以有多个文档对象。

所以一个窗口下只有一个window.location.href,但是可能有多个document.URL、document.location.href

2、window.location.href和document.location.href可以被赋值,然后跳转到其它页面,document.URL只能读不能写

  • getElementById():参数 ID 必须跟元素在页面中的 id 属性值完全匹配,包括大小写

  • getElementsByTagName():在 HTML 文档中,这个方法返回一个
    HTMLCollection 对象。

HTMLCollection 与 NodeList
let images = document.getElementsByTagName("img");

共同点:都可以 item()、 [x] length
alert(images.length); // 图片数量
alert(images[0].src); // 第一张图片的 src 属性
alert(images.item(0).src); // 同上

区别
HTMLCollection 对象还有一个额外的方法 namedItem()

let myImage = images.namedItem(“myImage”);
let myImage = images[“myImage”];

文档写入

write()和 writeln()方法经常用于动态包含外部资源

过 document.write()向文档中输出内容。如果是在页面加
载完之后再调用 document.write(),则输出的内容会重写整个页面

let div = document.getElementById(“myDiv”);
alert(div.tagName); // “DIV”
div.tagName 实际上返回的是"DIV"而不是
“div”。在 HTML 中,元素标签名始终以全大写表示;在 XML(包括 XHTML)中,标签名始终与源
代码中的大小写一致
if (element.tagName.toLowerCase() == “div”){ // 推荐,适用于所有文档
// 做点什么
}

let div = document.getElementById(“myDiv”);
alert(div.id); // “myDiv”
alert(div.className); // “bd”!!!!!!!!!!!!
alert(div.title); // “Body text”
alert(div.lang); // “en”
alert(div.dir); // “ltr”

取得属性

getAttribute()、setAttribute()和 removeAttribute()。

let div = document.getElementById(“myDiv”);
alert(div.getAttribute(“id”)); // “myDiv”
alert(div.getAttribute(“class”)); // “bd”
alert(div.getAttribute(“title”)); // “Body text”
alert(div.getAttribute(“lang”)); // “en”
alert(div.getAttribute(“dir”)); // “ltr”

“className”(className 是作为对象属性时才那么拼写的)。如果给定的属性不存在,则 getAttribute()
返回 null。

let value = div.getAttribute(“my_special_attribute”);

attributes 属性

Element 类型是唯一使用 attributes 属性的 DOM 节点类型

  • getNamedItem(name),返回 nodeName 属性等于 name 的节点;
  • removeNamedItem(name),删除 nodeName 属性等于 name 的节点;
  • setNamedItem(node),向列表中添加 node 节点,以其 nodeName 为索引;
  • item(pos),返回索引位置 pos 处的节点。

attributes 属性中的每个节点的 nodeName 是对应属性的名字,nodeValue 是属性的值。比如,
要取得元素 id 属性的值,可以使用以下代码:let id = element.attributes.getNamedItem(“id”).nodeValue;

下面是使用中括号访问属性的简写形式:
let id = element.attributes[“id”].nodeValue;
一般来说,因为使用起来更简便,通常开发者更喜欢使用 getAttribute()、removeAttribute()
和 setAttribute()方法,而不是刚刚介绍的 以上的NamedNodeMap 对象的方法。

attributes 属性最有用的场景是需要迭代元素上所有属性的时候。这时候往往是要把 DOM 结构
序列化为 XML 或 HTML 字符串
function outputAttributes(element) {
let pairs = [];
for (let i = 0, len = element.attributes.length; i < len; ++i) {
const attribute = element.attributes[i];
pairs.push(${attribute.nodeName}="${attribute.nodeValue}");
}
return pairs.join(" ");
}
这个函数使用数组存储每个名/值对,迭代完所有属性后,再将这些名/值对用空格拼接在一起。(这
个技术常用于序列化为长字符串。)

创建元素

let div = document.createElement(“div”);

div.id = “myNewDiv”;
div.className = “box”;
要把元素添加到文档树,可以使用 appendChild()、insertBefore()或 replaceChild()。
元素被添加到文档树之后,浏览器会立即将其渲染出来。之后再对这个元素所做的任何修改,都会立即在浏览器中反映出来。

for (let i = 0, len = element.childNodes.length; i < len; ++i) {
if (element.childNodes[i].nodeType == 1) {
// 执行某个操作
}
}
以上代码会遍历某个元素的子节点,并且只在 nodeType 等于 1(即 Element 节点)时执行某个操作。

动态脚本

动态脚本就是在页面初始加载时不存在,之后又通过 DOM 包含的脚本
有两种方式通过

可以像这样通过 DOM 编程创建这个节点:
let script = document.createElement(“script”);
script.src = “foo.js”;
document.body.appendChild(script)

另一个动态插入 JavaScript 的方式是嵌入源代码

let script = document.createElement(“script”);
script.appendChild(document.createTextNode(“function sayHi(){alert(‘hi’);}”));
document.body.appendChild(script);

var script = document.createElement(“script”);
var code = “function sayHi(){alert(‘hi’);}”;
try {
script.appendChild(document.createTextNode(“code”));
} catch (ex){
script.text = “code”;
}
document.body.appendChild(script);

于是,我们就可以
抽象出一个跨浏览器的函数:
function loadScriptString(code){
var script = document.createElement(“script”);
script.type = “text/javascript”;
try {
script.appendChild(document.createTextNode(code));
} catch (ex){
script.text = code;
}
document.body.appendChild(script);
}
这个函数可以这样调用:
loadScriptString(“function sayHi(){alert(‘hi’);}”);

动态css

let style = document.createElement(“style”);
style.type = “text/css”;
try{
style.appendChild(document.createTextNode(“body{background-color:red}”));
} catch (ex){
style.styleSheet.cssText = “body{background-color:red}”;
}
let head = document.getElementsByTagName(“head”)[0];
head.appendChild(style);

function loadStyleString(css){
let style = document.createElement(“style”);
style.type = “text/css”;
try{
style.appendChild(document.createTextNode(css));
} catch (ex){
style.styleSheet.cssText = css;
}
let head = document.getElementsByTagName(“head”)[0];
head.appendChild(style);
}
可以这样调用这个函数:
loadStyleString(“body{background-color:red}”);

DOM 扩展

querySelector()

// 取得元素
let body = document.querySelector(“body”);
// 取得 ID 为"myDiv"的元素
let myDiv = document.querySelector("#myDiv");
// 取得类名为"selected"的第一个元素
let selected = document.querySelector(".selected");
// 取得类名为"button"的图片
let img = document.body.querySelector(“img.button”);
在 Document 上使用 querySelector()方法时,会从文档元素开始搜索;在 Element 上使用
querySelector()方法时,则只会从当前元素的后代中查询。

querySelectorAll()

querySelectorAll()方法跟 querySelector()一样,也接收一个用于查询的参数,但它会返回
所有匹配的节点,而不止一个。这个方法返回的是一个 NodeList 的静态实例。
再强调一次,querySelectorAll()返回的 NodeList 实例一个属性和方法都不缺,但它是一
个静态的“快照”,而非“实时”的查询。这样的底层实现避免了使用 NodeList对象可能造成的性能问题。

matches()方法(在规范草案中称为 matchesSelector())接收一个 CSS 选择符参数,如果元素
匹配则该选择符返回 true,否则返回 false。例如:
if (document.body.matches(“body.page1”)){
// true
}
使用这个方法可以方便地检测某个元素会不会被 querySelector()或 querySelectorAll()方
法返回。
所有主流浏览器都支持 matches()

H5 新增

之前 要操作类名,可以通过 className 属性实现添加、删除和替换。

...

// 要删除"user"类
let targetClass = “user”;
// 把类名拆成数组
let classNames = div.className.split(/\s+/);
// 找到要删除类名的索引
let idx = classNames.indexOf(targetClass);
// 如果有则删除
if (idx > -1) {
classNames.splice(i,1);
}
// 重新设置类名
div.className = classNames.join(" ");

HTML5 通过给所有元素增加 classList 属性

  • add(value)向类名列表中添加指定的字符串值value。如果这个值已经存在,则什么也不做。
  • contains(value),返回布尔值,表示给定的 value 是否存在。
  • remove(value),从类名列表中删除指定的字符串值 value。
  • toggle(value),如果类名列表中已经存在指定的 value,则删除;如果不存在,则添加。

这样以来,前面的例子中那么多行代码就可以简化成下面的一行:
div.classList.remove(“user”);
// 删除"disabled"类
div.classList.remove(“disabled”);
// 添加"current"类
div.classList.add(“current”);

// 切换"user"类
div.classList.toggle(“user”);
// 检测类名
if (div.classList.contains(“bd”) && !div.classList.contains(“disabled”)){
// 执行操作
)
// 迭代类名
for (let class of div.classList){
doStuff(class);
}

scrollIntoView()

scrollIntoView()方法存在于所有 HTML 元素上,可以滚动浏览器窗口或容器元素以便包含元
素进入视口

alignToTop 是一个布尔值。

  • true:窗口滚动后元素的顶部与视口顶部对齐。
  • false:窗口滚动后元素的底部与视口底部对齐。
  • scrollIntoViewOptions 是一个选项对象。
  • behavior:定义过渡动画,可取的值为"smooth"和"auto",默认为"auto"。
  • block:定义垂直方向的对齐,可取的值为"start"、“center”、“end"和"nearest”,默
    认为 “start”。
  • inline:定义水平方向的对齐,可取的值为"start"、“center”、“end"和"nearest”,默
    认为 “nearest”。
  • 不传参数等同于 alignToTop 为 true。

// 确保元素可见
document.forms[0].scrollIntoView();
// 同上
document.forms[0].scrollIntoView(true);
document.forms[0].scrollIntoView({block: ‘start’});
// 尝试将元素平滑地滚入视口
document.forms[0].scrollIntoView({behavior: ‘smooth’, block: ‘start’});
这个方法可以用来在页面上发生某个事件时引起用户关注。把焦点设置到一个元素上也会导致浏览
器将元素滚动到可见位置。

contains()方法

如果目标节点是被搜索节点的后代,contains()返回 true,否则返回 false。下面看一个例子:
console.log(document.documentElement.contains(document.body)); // true

如前所述,滚动是 HTML5 之前 DOM 标准没有涉及的领域。虽然 HTML5 把 scrollIntoView()
标准化了,但不同浏览器中仍然有其他专有方法。比如,scrollIntoViewIfNeeded()作 为
HTMLElement 类型的扩展可以在所有元素上调用。scrollIntoViewIfNeeded(alingCenter)会在
元素不可见的情况下,将其滚动到窗口或包含窗口中,使其可见;如果已经在视口中可见,则这个方法
什么也不做。如果将可选的参数 alingCenter 设置为 true,则浏览器会尝试将其放在视口中央。Safari、
Chrome 和 Opera 实现了这个方法。
下面使用 scrollIntoViewIfNeeded()方法的一个例子:
// 如果不可见,则将元素可见
document.images[0].scrollIntoViewIfNeeded();
考虑到 scrollIntoView()是唯一一个所有浏览器都支持的方法,所以只用它就可以了。

DOM 样式属性和方法

myDiv.style.cssText = “width: 25px; height: 100px; background-color: green”;
console.log(myDiv.style.cssText);

滚动尺寸

有些元素,比如

无须任何代码就可以自动滚动,而其他元素则需要使用 CSS 的 overflow 属性令其滚动。滚动 尺寸相关的属性有如下 4 个。  scrollHeight,没有滚动条出现时,元素内容的总高度。  scrollLeft,内容区左侧隐藏的像素数,设置这个属性可以改变元素的滚动位置。  scrollTop,内容区顶部隐藏的像素数,设置这个属性可以改变元素的滚动位置。  scrollWidth,没有滚动条出现时,元素内容的总宽度。
确定元素尺寸
  • getBoundingClientRect()包含6 个属性:left、top、right、bottom、height 和width。

事件

因为属性的值是 JavaScript 代码,所以不能在未经转义的情况下使用 HTML 语 法字符,比如和号(&)、双引号(")、小于号(<)和大于号(>)。此时,为了避免使用 HTML 实体, 可以使用单引号代替双引号。如果确实需要使用双引号,则要把代码改成下面这样:

以这种方式指定的事件处理程序有一些特殊的地方。首先,会创建一个函数来封装属性的值。这个
函数有一个特殊的局部变量 event,其中保存的就是 event 对象

  • addEventListener

  • removeEventListener
    主要优势是可以为同一个事件添加多个事件处理程序。
    这个事件处理程序同样在被附加到的元素的作用域中运行 this 指当前元素。

let btn = document.getElementById(“myBtn”);
btn.addEventListener(“click”, () => {
console.log(this.id);
}, false);
btn.addEventListener(“click”, () => {
console.log(“Hello world!”);
}, false);

这里给按钮添加了两个事件处理程序。多个事件处理程序以添加顺序来触发,因此前面的代码会先
打印元素 ID,然后显示消息“Hello world!”。
通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添
加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除,如下面的例
子所示:
let btn = document.getElementById(“myBtn”);
btn.addEventListener(“click”, () => {
console.log(this.id);
}, false);
// 其他代码
btn.removeEventListener(“click”, function() { // 没有效果!
console.log(this.id);
}, false);

在事件处理程序内部,this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际
目标。如果事件处理程序直接添加在了意图的目标,则 this、currentTarget 和 target 的值是一样
的。下面的例子展示了这两个属性都等于 this 的情形:
let btn = document.getElementById(“myBtn”);
btn.onclick = function(event) {
console.log(event.currentTarget === this); // true
console.log(event.target === this); // true
};
上面的代码检测了 currentTarget 和 target 的值是否等于 this。因为 click 事件的目标是按
钮,所以这 3 个值是相等的。如果这个事件处理程序是添加到按钮的父节点(如 document.body)上,
那么它们的值就不一样了。比如下面的例子在 document.body 上添加了单击处理程序:
document.body.onclick = function(event) {
console.log(event.currentTarget === document.body); // true
console.log(this === document.body); // true
console.log(event.target === document.getElementById(“myBtn”)); // true
};
这种情况下点击按钮,this 和 currentTarget 都等于 document.body,这是因为它是注册事件
处理程序的元素。而 target 属性等于按钮本身,这是因为那才是 click 事件真正的目标。由于按钮
本身并没有注册事件处理程序,因此 click 事件冒泡到 document.body,从而触发了在它上面注册的
处理程序。

event.type:click、mouseover 和 mouseout。。。。。。。

preventDefault()方法用于阻止特定事件的默认动作

stopPropagation()

load 事件

第一种是 JavaScript 方式,如下所示:
window.addEventListener(“load”, (event) => {
console.log(“Loaded!”);
});
第二种指定 load 事件处理程序的方式是向元素添加 onload 属性

图片上也会触发load事件,包括DOM中的图片和非DOM中的图片。可以在HTML中直接给
元素的 onload 属性指定事件处理程序,比如:

使用 JavaScript 也可以为图片指定事件处理程序:
let image = document.getElementById(“myImage”);
image.addEventListener(“load”, (event) => {
console.log(event.target.src);
});

在通过 JavaScript 创建新元素时,也可以给这个元素指定一个在加载完成后执行的事件处理
程序。在这里,关键是要在赋值 src 属性前指定事件处理程序,如下所示:
window.addEventListener(“load”, () => {
let image = document.createElement(“img”);
image.addEventListener(“load”, (event) => {
console.log(event.target.src);
});
document.body.appendChild(image);
image.src = “smile.gif”;
});

同样的技术也适用于 DOM0 的 Image 对象。在 DOM 出现之前,客户端都使用 Image 对象预先加载图片。可以像使用前面(通过 createElement()方法创建)的元素一样使用 Image 对象,只
是不能把后者添加到 DOM 树。下面的例子使用新 Image 对象实现了图片预加载:

window.addEventListener(“load”, () => {
let image = new Image();
image.addEventListener(“load”, (event) => {
console.log(“Image loaded!”);
});
image.src = “smile.gif”;
});

但也要注意,在事件处理程序中删除按钮会阻止事件冒泡。只有事件目标仍然存在于文档中时,事件才会冒泡。

##表单

有几种方式可以取得对元素的引用。最常用的是将表单当作普通元素为它指定一个 id 属
性,从而可以使用 getElementById()来获取表单,比如:
let form = document.getElementById(“form1”);
// 取得页面中的第一个表单
let firstForm = document.forms[0];
// 取得名字为"form2"的表单
let myForm = document.forms[“form2”];

表单元素可以像页面中的其他元素一样使用原生 DOM 方法来访问。此外,所有表单元素都是表单
elements 属性(元素集合)中包含的一个值
let form = document.getElementById(“form1”);
// 取得表单中的第一个字段
let field1 = form.elements[0];
// 取得表单中名为"textbox1"的字段
let field2 = form.elements[“textbox1”];
// 取得字段的数量
let fieldCount = form.elements.length;

提交表单
Submit Form 如果表单中有上述任何一个按钮,且焦点在表单中某个控件上,则按回车键也可以提交表单。(textarea 控件是个例外,当焦点在它上面时,按回车键会换行。) 阻止这个事件的默认行为可以取消提交表单。例如, 下面的代码会阻止表单提交: let form = document.getElementById("myForm"); form.addEventListener("submit", (event) => { // 阻止表单提交 event.preventDefault(); });

也可以通过编程方式在 JavaScript 中调用 submit()方法来提交表单。可以在任何时候调用
这个方法来提交表单,而且表单中不存在提交按钮也不影响表单提交。
let form = document.getElementById(“myForm”);
// 提交表单
form.submit();

检测有效性

使用 checkValidity()方法可以检测表单中任意给定字段是否有效。这个方法在所有表单元素上
都可以使用,如果字段值有效就会返回 true,否则返回 false。判断字段是否有效的依据是本节前面
提到的约束条件,因此必填字段如果没有值就会被视为无效,而字段值不匹配 pattern 属性也会被视
为无效
if (document.forms[0].elements[0].checkValidity()){
// 字段有效,继续
} else {
// 字段无效
}
要检查整个表单是否有效,可以直接在表单上调用 checkValidity()方法。这个方法会在所有字
段都有效时返回 true,有一个字段无效就会返回 false:
if(document.forms[0].checkValidity()){
// 表单有效,继续
} else {
// 表单无效
}

禁用验证

通过指定 novalidate 属性可以禁止对表单进行任何验证:

如果一个表单中有多个提交按钮,那么可以给特定的提交按钮添加 formnovalidate 属性,指定 通过该按钮无须验证即可提交表单: 在这个例子中,第一个提交按钮会让表单像往常一样验证数据,第二个提交按钮则禁用了验证,可 以直接提交表单。我们也可以使用 JavaScript 来设置这个属性: // 关闭验证 document.forms[0].elements["btnNoValidate"].formNoValidate = true;
选择框是使用和元素创建的。
表单字段的公共能力之外又提供了以下属性和方法。

 add(newOption, relOption):在 relOption 之前向控件中添加新的。
 multiple:布尔值,表示是否允许多选,等价于 HTML 的 multiple 属性。
 options:控件中所有元素的 HTMLCollection。
 remove(index):移除给定位置的选项。
 selectedIndex:选中项基于 0 的索引值,如果没有选中项则为–1。对于允许多选的列表,始
终是第一个选项的索引。
 size:选择框中可见的行数,等价于 HTML 的 size 属性

函数

箭头函数虽然语法简洁,但也有很多场合不适用。箭头函数不能使用 arguments、super 和
new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昔人'

你的鼓励将是我创造的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值