break | do | in | typeof |
---|---|---|---|
case | else | instanceof | var |
catch | export | new | void |
class | extends | return | while |
const | finally | super | with |
continue | for | switch | yield |
default | if | throw | this |
function | debugger | delete | import |
try |
始终保留 | 严格模式下保留 | 模块代码中保留 | |
---|---|---|---|
enum | implements | package | await |
public | interface | ||
protected | private | ||
static | let |
// var的作用域
// var声明提升
// 混用var与let
// for循环中的let声明
// for-of 和 for-in
// Object.freeze() 再给属性赋值时不会报错 但会赋值失败
const o1 = {
age: 13,
};
const o2 = Object.freeze({
age: 14,
});
o1.age = 0;
o2.age = 0;
o2.name = `xiaoming`;
console.log(`${o1.age} ${o2.age} ${02.name}`); // 0 14 undefined
使用typeof返回的字符串及其意义
| 字符串 | 意义 |
| — | — |
| “undefined” | 值未定义 |
| “boolean” | 值为布尔值 |
| “string” | 值为字符串 |
| “number” | 值为数值 |
| “object” | 值为对象或null |
| “function” | 值为函数 |
| “symbol” | 值为符号 |
具体用例:
// typeof 操作符
// 包含undefined值的变量与未定义变量的区别
// 明确检测undefined这个字面值
// null与undefined表面相等
// 明确检测null这个字面值
// 0、+0、-0 相除
// infinity情况
// isNaN() 函数
// 布尔值
// 数值
// null
// undefined
// 字符串
// 对象
// 空字符串
// 数字+其他字符 组成的字符串
// 其他进制数
// 第一次出现的小数点是有效的,第二次出现的小数点是无效的
// 只解析十进制数,不能指定底数
// 始终忽略字符串开头的0
// 若字符串表示整数(没有小数点或者小数点后面只有0,则返回整数)
\n | 换行 |
---|---|
\t | 制表 |
\b | 退格 |
\r | 回车 |
\f | 换页 |
\\ | 反斜杠 |
\’ | 单引号 |
\" | 双引号 |
\` | 反引号 |
\xnn | 以十六进制编码nn表示的字符(n是十六进制数字0~F) |
\unnnn | 以十六进制编码nnnn表示的Unicode字符 |
// toString()
// String()
// 定义模板 HTML模板 可以安全地插入到HTML中
// 保持内部空格
${[JavaScript表达式]}
来实现// toString()转换
// 表达式中调用函数和方法
// 表达式中插入自己之前的值
// 标签函数
字符串内容
可以直接获取原始的模板字面量,而不是被转移后的字符表示// typeof操作符
// 创建Symbol(`字符串参数`)实例并将其用作对象的新属性 即使参数相同,Symbol也是不同的
// 全局符号注册表 Symbol.for(`字符串参数`)方法
// Symbol.asyncIterator
// 一个方法,该方法返回对象默认的AsyncIterator。由for-await-of使用。实现了异步迭代器API的函数
// Symbol.hasInstance
// 一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由操作符instanceof操作符使用。
// instanceof操作符可以用来确定一个对象实例的原型链上是否有原型
// Symbol.isConcatSpreadable
// 一个布尔值,如果是true或真值,类数组对象会被打平到数组实例,用Array.prototype.concat()打平
// 如果是false或假值,整个对象被追加到数组末尾
// Symbol.iterator
// 一个方法,该方法返回对象默认的迭代器。由for-of语句使用。这个符号实现了迭代器API的函数
// Symbolmatch
// 一个正则表达式方法,该方法用正则表达式去匹配字符串。由String.prototype.match()方法使用
// Symbol.replace
// 一个正则表达式方法,该方法替换一个字符串中的匹配字符串。由String.prototype.replace()方法使用
// Symbol.search
// 一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由String.prototype.search()使用
// Symbol.species
// 一个函数值,该函数作为创建派生对象的构造函数
// Symbol.split
// 一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split()使用
// Symbol.toPrimitive
// 一个方法,该方法将对象转换为相应的原始值。由ToPrimitive抽象对象使用
// Symbol.toStringTag
// 一个字符串,该字符串用于创建对象默认字符串描述。由内置方法Object.prototype.toString()使用
let s = 1.1;
console.log(--s); // 0.10000000000000009
// 第一个不是null或者undefined 第二个值就不管了
let myObject = preferredObject || backupObject
let max = (num > num2) ? num1 : num2;
// 若(num > num2)为true (num > num2) ? num1 : num2===num1
// 若(num > num2)为false (num > num2) ? num1 : num2===num2
// 语法:for(property in expression) statement
let o = {
name: `xiaoming`,
age: 11,
height: 180,
weight: 150,
};
for (const propName in o) {
console.log(`${propName}`);
}
// name
// age
// height
// weight
// 语法:for(property of expression) statement
for (const el of[1, 4, 3, 2]) {
console.log(el);
}
// 1
// 4
// 3
// 2
// break
let num = 0;
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break;
}
num++;
}
}
console.log(num);// 95
// break+标签 如果不用标签 会退出一层循环 这个标签在循环最外层 因此退出到了最外层循环
num = 0;
outermost:
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break outermost;
}
num++;
}
}
console.log(num);// 55
// continue
num = 0;
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
continue;
}
num++;
}
}
console.log(num);
// continue+标签 如果不用标签,会结束本轮j循环进入j+1循环
// 用了标签,结束本轮i循环,进入i+1循环
num = 0;
outermost2:
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
continue outermost2;
}
num++;
}
}
console.log(num);
// 案例中switch语句是布尔值 条件是表达式 若条件为true 与switch参数相等 就进入语句
let num = 25;
switch (true) {
case num < 0:
console.log(`num<0`);
breakl;
case num >= 0 && num < 10:
console.log(`0<=num<10`);
break;
case num >= 10 && num < 20:
console.log(`10<=num<20`);
break;
default:
console.log(`num>=20`);
}
// 原始类型使用new的初始化
let name = new String(`xiaoming`);
console.log(typeof name);//Object
// 创建一个日期对象
let now = new Date(); // Mon Jul 13 1987 20:29:48 GMT+0900 (中国夏令时间)
// Date.now()方法返回执行时日期和时间的毫秒数 可用于代码分析(获得时间差)
console.log(Date.now()); // 1657715845530
let t1 = now.getTime(); // 1657715388159
// 返回四位数年
let y1 = now.getFullYear(); // 2022
// 返回UTC日期的四位数年
let y2 = now.getUTCFullYear(); // 2022
// 设置日期的年
let y3 = now.setFullYear(1987); // 553174258602
// 设置日期的年后 now对应的年改变了
let y4 = now.getFullYear(); // 1987
//定义倒数函数
function countDown(time) {
var nowTime = +new Date(); //当前时间
var inputTime = +new Date(time); //用户输入的时间
var times = (inputTime - nowTime) / 1000; //由毫秒得到秒
var d = parseInt(times / 60 / 60 / 24); //天
d = d < 10 ? '0' + d : d;
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
var m = parseInt(times / 60 % 60); //分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); //秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒'; //返回剩余时间
}
// 方法1
let pattern1 = /[bc]at/i;
// 方法2
let pattern2 = new RegExp("[bc]at","i");
// 方法3
const re1 = /cat/g;
const re2 = new RegExp(re1,"i");
g | 全局模式,表示查找字符串的全部内容,而不是匹配一个就结束 |
---|---|
i | 不区分大小写 |
m | 多行模式,查到一行末会继续查找 |
y | 粘附模式,只查找从lastIndex开始及之后的字符串(最后一个索引对应的值为首) |
u | Unicode模式,启用Unicode匹配 |
s | dotAll模式,表示元字符,匹配任何字符串(包括\n和\r) |
let stringValue = `today is a sunny day,it's good to have a travel`;
// 存放目标字符串的位置
let position = new Array();
let pos = stringValue.indexOf(`a`);
// 找不到就停止
while (pos > -1) {
console.log(pos);
position.push(pos);
pos = stringValue.indexOf(`a`, pos + 1);// 往后一位
}
console.log(position); // [ 3, 9, 18, 35, 39, 43 ]
for (const c of `abcd`){
console.log(c);
}
// a
// b
// c
// d
Math.E | 自然对数的基数e的值 |
---|---|
Math.LN10 | 10为底的自然对数 |
Math.LN2 | 2为底的自然对数 |
Math.LOG2E | 以2为底e的对数 |
Math.LOG10E | 以10为底e的对数 |
Math.PI | pi的值 |
Math.SQRT1_2 | 1/2的平方根 |
Math.SQRT2 | 2的平方根 |
// 函数 功能:生成指定范围的随机数
function selectFrom(lowerValue, upperValue) {
let choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
Math.abs(x) | 绝对值 |
---|---|
Math.exp(x) | e的x次幂 |
Math.expm1(x) | e的x次幂-1 |
Math.log(x) | x的自然对数 |
Math.log1p(x) | x的自然对数+1 |
Math.pow(x,power) | x的power次幂 |
Math.hypot(…nums) | 每个数平方和的平方根 |
Math.clz32(x) | 32位整数x的前置0的数量 |
Math.sign(x) | x的符号 |
Math.trunc(x) | x的整数部分 |
Math.sqrt(x) | 平方根 |
Math.cbrt(x) | 立方根 |
Math.acos(x) | 反余弦 |
Math.acosh(x) | 反双曲余弦 |
Math.asin(x) | 反正弦 |
Math.asinh(x) | 反双曲正弦 |
Math.atan(x) | 反正切 |
Math.atanh(x) | 反双曲正切 |
Math.atan2(x) | y/x的反正切 |
Math.cos(x) | 余弦 |
Math.sin(x) | 正弦 |
Math.tan(x) | 正切 |
const a1 = [1, 2, 3, 4];
// 使用映射函数参数,直接增强新数组的值
const a2 = Array.from(a1, x => x - Math.pow(x, 2));
// 指定this的值
const a3 = Array.from(a1, function(x) {
return x ** this.exponent;
}, { exponent: 2 });
console.log(a2); // [ 0, -2, -6, -12 ]
console.log(a3); // [ 1, 4, 9, 16 ]
let a = [1, 2, 3, 4, 5];
let aEntries = Array.from(a.entries());
let aKeys = Array.from(a.keys());
let aValues = Array.from(a.values());
console.log(aEntries); // [ [ 0, 1 ], [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
console.log(aKeys); // [ 0, 1, 2, 3, 4 ]
console.log(aValues); // [ 1, 2, 3, 4, 5 ]
// 利用解构拆分键值对
for (const [idx, element] of a.entries()) {
console.log(`${idx}:${element}`);
}
// 0:1
// 1:2
// 2:3
// 3:4
// 4:5
let values = [0, 4, 33, -9, -9, 0];
// 反向排序 使用箭头函数+条件操作符简化代码
values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
console.log(values); // [ 33, 4, 0, 0, -9, -9 ]
// 正向排序
values.sort((a, b) => a > b ? 1 : a < b ? -1 : 0);
console.log(values); // [ -9, -9, 0, 0, 4, 33 ]
const a1 = [1, 2, 3, 4];
const a2 = [5, 6];
a2[Symbol.isConcatSpreadable] = false;
const a3 = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: 5,
1: 6
};
console.log(a1.concat(a2));
console.log(a1.concat(a3));
const people = [{
name: `xiaoming`,
age: 11,
},
{
name: `xiaowang`,
age: 27,
},
];
console.log(people.findIndex((element, index, array) => element.age < 17));
// 0
console.log(people.find((element, index, array) => element.age < 17));
// { name: 'xiaoming', age: 11 }
// every()方法
const a = [1, 2, 3, 4];
console.log(a.every((item, index, array) => item < 5));// true
const a1 = [1, 2, 3, 4];
// 归并函数接收4个参数
// prev:上一个归并值 cur:当前项 index当前项的索引 array:数组本身
console.log(a1.reduce((prev, cur, index, array) => prev + cur));
// 10
ElementType | 字节 | 说明 | 范围 |
---|---|---|---|
Int8 | 1 | 8位有符号整数 | -128-127 |
Uint8 | 1 | 8位无符号整数 | 0-255 |
Int16 | 2 | 16位有符号整数 | -32768-32767 |
Uint16 | 2 | 16位无符号整数 | 0-65535 |
Int32 | 4 | 32位有符号整数 | -2147483648-2147483647 |
Uint32 | 4 | 32位无符号整数 | 0-4294967295 |
Float32 | 4 | 32位IEEE-754浮点数 | -3.4e+38~+3.4e+38 |
Float64 | 8 | 64位IEEE-754浮点数 | -1.7e+308~+1.7e+308 |
const a = [1, 2, 3, "4", { b: 5, c: 6 }];
const a1 = [1];
// 调用数组的[Symbol.iterator]属性这个函数 返回一个迭代器
let iter = a[Symbol.iterator]();
let iter2 = iter[Symbol.iterator]();
// 迭代器与迭代器的迭代器全等
console.log(iter2 === iter); // true
// 通过break停止了迭代 但迭代器本身是没有停止的
for (const i of iter) {
console.log(i);
if (i > 2) {
console.log(`stop or not?`);
break;
}
}
// 1
// 2
// 3
// stop or not?
for (const i of iter) {
console.log(i);
}
// 4
// { b: 5, c: 6 }
// 创建一个生成器
function* generatorFn(initial) {
console.log(initial);
console.log(yield);
}
// 实例化一个生成器对象
const generatorObject = generatorFn('foo');
// 第一个next()方法启动生成器对象,此传入值无效
generatorObject.next(`zro`);// foo
generatorObject.next(`abb`);// abb
// 因为最后没有代码了,再调用next()也没有用了
generatorObject.next(`cdd`);
function* generatorFn() {
// 使用yield可以很方便地进行迭代
yield*[1, 2, 3];
}
const g = generatorFn();
for (const k of g) {
console.log(k);
}
// 1
// 2
// 3
function* generatorFn(n) {
if (n > 0) {
yield* generatorFn(n - 1);
yield n - 1;
}
}
const g = generatorFn(3);
for (const k of g) {
console.log(k);
}
// 0
// 1
// 2
function* generatorFn() {
// 注意这个是生成器的返回 后面还有一个生成器的方法return() 是用来终止生成器的
return `foo`;
}
const g = generatorFn();
console.log(g.next()); // { value: 'foo', done: true }
function* generatorFn() {
for (const x of[1, 2, 3]) {
try {
yield x;
} catch (e) {}
}
}
const g = generatorFn();
console.log(g.next());
// 这个结果与我想象中不太一样 调用throw()方法后2还是被返回了 我不理解
// 并且如果调用throw()方法 生成器内部不处理的话 编辑器是会报错的
// 如果再第一次调用next()方法之前就使用throw()方法的话 也会报错 因为此时生成器还没有启动
console.log(g.throw());
console.log(g.next());
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
let person = {};
Object.defineProperty(person, `age`, {
configurable: false,
enumerable: true,
writable: true,
value: 3,
});
console.log(person.age);
Object.defineProperty(person, `name`, {
value: 2,
});
console.log(person.age);
// 函数 用于比较一个或多个值是否相等
function recursivelyCheckEqual(x, ...rest) {
return Object.is(x, rest[0]) && (rest.length < 2 || recursivelyCheckEqual(...rest));
}
console.log(recursivelyCheckEqual(1, ...[1, 1, 1])); // true
console.log(recursivelyCheckEqual(...[1, 1, 1, 1])); // true
console.log(recursivelyCheckEqual([1, 1, 1, 1])); // false
console.log(recursivelyCheckEqual(1)); // false rest[0]是undefined,即没有第二个参数的情况下,默认为undefined
console.log(recursivelyCheckEqual(1, 1, 1, 1, 1, 1)); // true
const methodKey = `sayName`;
let person = {
name_: ``,
// 简写了方法名
get name() {
return this.name_;
},
// 由name属性的get函数进行简单的获取 没有这个get函数并不影响设置this.name_的值
// 再由name属性的set函数设置this.name_
// 这样调用sayName()的时候this.name_就是`Matt`了
set name(name) {
this.name_ = name;
},
// 这里使用可计算属性
[methodKey]() {
console.log(`My name is ${this.name_}`);
}
};
person.name = `Matt`; // get和set都是访问器属性name的函数
person.sayName(); // My name is Matt
let person = {
name: `Matt`,
age: 16,
job: {
title: `software engineer`,
}
};
let personCopy = {};
// 注意:这里是把一个对象的引用直接赋值给personCopy了 也就是只传了地址 一改全改
// 个人认为这里本质上是用一个对象作为过渡
// 但是我不明白为什么personCopy.name可以使用 不是没有定义吗
// 还是说 这一整块放进去括号里面 就默认声明了?
({
name: personCopy.name,
age: personCopy.age,
job: personCopy.job
} = person);
console.log(personCopy);
// 构造器函数
function Person(name, age, job) {
console.log(this);
this.name = name;
this.age = age;
this.job = job;
this.sayNmae = function() {
console.log(this.name);
}
}
// 实例化对象 类型为Person
let person1 = new Person(`kitty`, `22`, `worker`); // Person {}
// 若直接调用函数 内部this指向为window
Person(`hello`, `3`, `play game`); // 指向window
// 函数 作用 确定某个属性是否存在于实例对象原型上
// Object是实例对象 name是属性名 为字符串
function hasPrototypeProperty(Object, name) {
return !Object.hasOwnProperty(name) && (name in Object);
}
function Person() {
Person.prototype.name = 'Mit';
Person.prototype.age = 13;
}
const person = new Person;
console.log(hasPrototypeProperty(person, `name`)); // true
person.name = `Code`;
console.log(hasPrototypeProperty(person, `name`)); // false
for (k in person) {
console.log(k);
}
// 4. 在SuperType的方法上面寻找 找不到
function SuperType() {
this.property = true;
}
// 5. 在SuperType的prototype的方法上面寻找 找到了 返回值是this.prototype 此值为true
SuperType.prototype.getSuperValue = function() {
return this.property;
};
// 1. 在SubType的方法上面寻找 找不到
function SubType() {
this.subProperty = false;
}
// 3. SubType的prototype定义在SuperType上面
SubType.prototype = new SuperType();
// 2. 在SubType的prototype的方法上面寻找 找不到
SubType.prototype.getSubValue = function() {
return this.subProperty;
};
let instance = new SubType();
// 目的:寻找getSuperValue方法
console.log(instance.getSuperValue()); // true
// 组合继承实现了数据的私有以及方法的共享
function SuperType(name) {
this.name = name;
this.color = [`black`, `blue`, `white`];
}
// 这是原型链 定义方法
SuperType.prototype.sayName = function() {
console.log(this.name);
};
// 这是盗用构造函数 定义属性
function SubType(name, age) {
SuperType.call(this, name);
// 增添新数据
this.age = age;
}
SubType.prototype = new SuperType();
// 向自己的原型链增添新方法
SubType.prototype.sayAge = function() {
console.log(this.age);
}
// 创建SubType的实例1
const instance1 = new SubType(`Marry`, 19);
instance1.color.shift();
console.log(instance1.color); // [ 'blue', 'white' ]
instance1.sayAge(); // 19
instance1.sayName(); // Marry
// 创建SubType的实例2
const instance2 = new SubType(`Tracy`, 23);
instance2.color.push(`gray`);
// 实例1与实例2的数据并不会互相影响
console.log(instance2.color); // [ 'black', 'blue', 'white', 'gray' ]
// 但实例1、实例2和原型链上的方法是共享的
instance2.sayAge(); // 23
instance2.sayName(); // Tracy
// 这个object()函数会创建一个临时的构造函数,将传入的对象赋值给这个构造函数的原型,
// 然后返回这个临时类型的一个实例
// 本质上,object()是对传入的对象进行了一次浅复制
function object(o) {
function F() {}
F.prototype = o;
return new F;
}
// 寄生式的核心函数
function inheritPrototype(SubType, SuperType) {
const prototype = new Object(SuperType.prototype); // 创建对象
prototype.constructor = SubType; // 增强对象
SubType.prototype = prototype; // 赋值对象
}
function SuperType(name) {
this.name = name;
this.color = [`black`, `blue`, `white`];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
// SubType.prototype = new SuperType(); 这一行被取代了
// 原本是直接创建一个SuperType实例作为SubType.prototype
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
}
const instance1 = new SubType(`Marry`, 19);
instance1.color.shift();
console.log(instance1.color); // [ 'blue', 'white' ]
instance1.sayAge(); // 19
instance1.sayName(); // Marry
const instance2 = new SubType(`Tracy`, 23);
instance2.color.push(`gray`);
console.log(instance2.color); // [ 'black', 'blue', 'white', 'gray' ]
instance2.sayAge(); // 23
instance2.sayName(); // Tracy
class Person {
constructor() {
this.locate = () => console.log(`instance`);
}
locate() {
console.log(`prototype`);
}
}
const p = new Person();
p.locate(); // instance
Person.prototype.locate(); // prototype
// 抽象基类
class Vehicle {
constructor() {
console.log(new.target);
if (new.target === Vehicle) {
throw new Error(`Vehicle cannot be directly instantiated`);
}
}
}
// 派生类
class Bus extends Vehicle {};
new Bus(); // [class Bus extends Vehicle]
// new Vehicle(); // Error: Vehicle cannot be directly instantiated
class Vehicle {}
let FooMixin = (Superclass) => class extends Superclass {
// 这里面放自己需要的代码
foo() {
console.log(`foo`);
}
};
let BarMixin = (Superclass) => class extends Superclass {
bar() {
console.log(`bar`);
}
};
let BazMixin = (Superclass) => class extends Superclass {
baz() {
console.log(`baz`);
}
};
// 通过这个辅助函数 将嵌套调用展开 不过这个函数我看不懂
function mix(BaseClass, ...Mixins) {
return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass);
}
class Bus extends mix(Vehicle, FooMixin, BarMixin, BazMixin) {}
let b = new Bus();
b.foo(); // foo
b.bar(); // bar
b.baz(); // baz
function King() {
console.log(this); // King {} 这是一个普通函数 且不是以方法的形式调用 this就指向函数本身
this.name = 'xiaoming';
setTimeout(() => {
console.log(this); // King { name: 'xiaoming' } 这是一个箭头函数 定义在定时器里面
// 但上下文据此推测是该函数
console.log(this.name); // xiaoming
}, 5000);
}
function Queen() {
console.log(this); // Queen {}
this.name = `xiaohong`;
setTimeout(function() {
// 这是一个普通函数 调用该函数的是定时器 因此该函数内部的this指向定时器
console.log(this);
// Timeout {
// _idleTimeout: 10000,
// _idlePrev: null,
// _idleNext: null,
// _idleStart: 51,
// _onTimeout: [Function (anonymous)],
// _timerArgs: undefined,
// _repeat: null,
// _destroyed: false,
// [Symbol(refed)]: true,
// [Symbol(kHasPrimitive)]: false,
// [Symbol(asyncId)]: 9,
// [Symbol(triggerId)]: 1
// }
console.log(this.name); // undefined
}, 10000)
}
new King();
new Queen();
// 这里是先执行函数命名表达式 因为它在括号里面
// 执行函数命名表达式的结果是返回一个函数的地址 跟函数表达式声明一样
// 其实这里括号不要也可以
const factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
});
// 首先 factorial是动不了的 其次动了也没有意义
// 要减少耦合 只是说赋值给别人后 别人也可以用
const anotherFactorial = factorial;
console.log(factorial); // [Function: f]
console.log(anotherFactorial(3));
// 优化后
`use strict`;
// 基础框架
function fib(n) {
return fibImpl(0, 1, n);
}
function fibImpl(a, b, n) {
if (n === 0) {
return a;
}
return fibImpl(b, a + b, n - 1);
}
console.log(fib(1000));
// 优化前
function fib(n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
console.log(fib(1000));
function MyObject() {
// 私有变量和私有函数
let privateVariable = 10;
function privateFunction() {
return false;
}
// 公有方法 此函数是与MyObject的活动对象关联的
// 在实例上调用此方法 使privateVariable自增 且调用privateFunction函数
// 实现了 公有方法访问私有数据
this.publicMethod = function() {
privateVariable++;
return privateFunction();
};
}
// 这是一个匿名函数 且此函数表达式立刻执行
(function() {
// 私有变量和私有函数
let privateVariable = 10;
function privateFunction() {
return false;
}
// 创建对象
MyObject = function() {};
// 通过prototype共享方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();
// 此函数是立即调用并返回函数表达式的值给singleton
// 通过singlrton就可以调用公有方法 进而使用私有数据 私有方法
let singleton = function() {
// 私有变量 私有函数
let privateVariable = 10;
function privateFunction() {
return false;
}
// 创建对象
let object = new CustomType();
// 增添特权/公有属性和方法
// 定义了一个匿名函数 这是一个闭包函数 与singleton的活动对象关联
object.publicMethod = function() {
privateVariable++;
return privateFunction();
};
// 返回对象 这个就是哈数表达式的返回值
return object;
}();
// 为避免期约卡在特定状态 可以设置定时退出功能
// 但是这玩意儿是会报错的 不好用
let p = new Promise((resolve, reject) => {
setTimeout(reject, 5000);
});
setTimeout(console.log, 0, p);
setTimeout(console.log, 6000, p);
// 这两种行为是等价的
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
该方法用于给期约添加onFinally处理程序,这个处理程序在期约状态改变时(转为解决或者拒绝)执行
此方法在大多数情况下表现为父期约的传递
let p1 = new Promise((resolve, reject) => {
console.log(`p1 executor`);
setTimeout(resolve, 1000);
});
p1.then(() => new Promise((resolve, reject) => {
console.log(`p2 executor`);
setTimeout(resolve, 1000);
}))
.then(() => new Promise((resolve, reject) => {
console.log(`p3 executor`);
setTimeout(resolve, 1000);
}))
.then(() => new Promise((resolve, reject) => {
console.log(`p4 executor`);
setTimeout(resolve, 1000);
}));
// 将生成期约的代码提取到一个工厂函数中
// 使用的时候只要修改工厂函数的名称与功能代码就可以了
function delayedResolve(str) {
return new Promise((resolve, reject) => {
console.log(str);
setTimeout(resolve, 1000);
});
}
delayedResolve(`p1 executor`)
.then(() => delayedResolve(`p2 executor`))
.then(() => delayedResolve(`p3 executor`))
.then(() => delayedResolve(`p4 executor`));
// Promise.all()
let p1 = Promise.all([
Promise.resolve(3),
Promise.resolve(4),
Promise.resolve(5)
]).then((values) => setTimeout(console.log, 0, values)); // [ 3, 4, 5 ]
let p2 = Promise.all([
Promise.resolve(3),
Promise.resolve(4),
Promise.reject(5)
]).then(null, (reason) => console.log(reason)); // 5
// Promise.race()
let p3 = Promise.race([
Promise.resolve(3),
Promise.resolve(4),
Promise.resolve(5)
]).then((values) => setTimeout(console.log, 0, values)); // 3
let p4 = Promise.race([
Promise.reject(3),
Promise.resolve(4),
Promise.resolve(5)
]).catch((reason) => setTimeout(console.log, 0, reason)); // 3
function addTwo(x) { return x + 2; }
function addThree(x) { return x + 3; }
function addFive(x) { return x + 5; }
// 利用扩展操作符进行参数收集
function compose(...fns) {
// 返回一个箭头函数 箭头函数参数为x
// 利用Array.reduce()方法进行归并
// 利用期约进行参数传递
return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x))
}
let addTen = compose(addTwo, addThree, addFive);
addTen(8).then(console.log); // 18
async function foo() {
console.log(2);
console.log(await Promise.resolve(8));
console.log(9);
}
async function bar() {
console.log(4);
console.log(await Promise.resolve(6));
console.log(7);
}
console.log(1);
foo();
console.log(3);
bar();
// 1
// 2
// 3
// 4
// 8
// 9
// 6
// 7
// 非阻塞暂停sleep
async function sleep(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
async function foo() {
const t0 = Date.now();
// 暂停执行1500ms
await sleep(1500);
console.log(Date.now() - t0);;
}
foo(); // 1509
window.scrollTo({
left: 0,
top: 300,
behavior: 'smooth'
});
// 有一个问题是我在调试的时候 behavior设置为auto它就不移动了
let num = 0;
let max = 10;
let incrementNumber = function() {
num++;
// 如果还没有达到最大值,再设置一个超时任务
if (num < max) {
// 但是吧 这里也是有缺陷的
// 里面的代码就得根据外面的数据来进行 挺不方便的
// 可以进行优化
setTimeout(incrementNumber, 500);
} else {
console.log(`Done`);
}
}
setTimeout(incrementNumber, 500);
alert(`alert`);
confirm(`confirm`);
prompt(`prompt`, `prompt`);
![image.png](https://img-blog.csdnimg.cn/img_convert/ae0d737818c59b6cb37ecce6131f70ea.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=127&id=Uiz9p&margin=[object Object]&name=image.png&originHeight=127&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2610&status=done&style=none&taskId=u116e502f-bacb-4027-b7a6-7e667d34838&title=&width=450)![image.png](https://img-blog.csdnimg.cn/img_convert/64b9c77918bf1fc6bec23b2333b3b38c.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=127&id=qrvhF&margin=[object Object]&name=image.png&originHeight=127&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=3341&status=done&style=none&taskId=u42e5c39a-37c7-493b-a99a-75f591c0b08&title=&width=450)![image.png](https://img-blog.csdnimg.cn/img_convert/8b5b5e71b3592bb86fb39b18e9853ac9.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=165&id=Bh9C2&margin=[object Object]&name=image.png&originHeight=165&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4258&status=done&style=none&taskId=ub6bffc96-c2e7-4a08-8869-e493bef9a93&title=&width=450)
location 对象属性 | 返回值 |
---|---|
location.herf | 获取或者设置 整个URL |
location.host | 返回主机(域名) |
location.port | 返回端口号 如果未写返回 空字符串 |
location.pathname | 返回路径 |
location.search | 返回参数 |
location.hash | 返回片段 #后面的内容 常见于链接 锚点 |
// 这就是查询字符串
let qs = `?q=javascript&num=10`;
// 通过new进行实例化
let searchParams = new URLSearchParams(qs);
// toString()
console.log(searchParams.toString()); // q=javascript&num=10
// has()
console.log(searchParams.has(`num`)); // true
// get()
console.log(searchParams.get(`num`)); // 10
// set()
searchParams.set(`page`, `3`); // q=javascript&num=10&page=3
console.log(searchParams.toString());
// delete()
searchParams.delete(`q`);
console.log(searchParams.toString()); // num=10&page=3
// 支持迭代
for (const param of searchParams) {
console.log(param);
// [ 'num', '10' ]
// [ 'page', '3' ]
}
<a href="javascript:;" name="aaa"></a>
<script>
// 文档中所有a标签
let as = document.anchors;
let length = document.anchors.length;
console.log(as, length); // HTMLCollection [a, aaa: a
// 复制并添加a标签在文档中
let a2 = document.querySelectorAll(`a`);
let a3 = a2[0].cloneNode();
let script = document.querySelector(`script`);
script.appendChild(a3);
console.log(as, length); // HTMLCollection(2) [a, a, aaa: a] 1
</script>
<a href="javascript:;" name="aaa"></a>
<script>
console.log(as, length); // HTMLCollection(3) [a, a, a, aaa: a] 1
// 确实会实时更新的 不必手动再获取一次 就连被赋值的变量都是如此
// 直接被HTMLCollection赋值的变量是会实时更新的
// 但是HTMLCollection的长度赋值的变量并不会实时更新
</script>
<script>
const div = document.createElement(`div`);
const node1 = document.createTextNode(`hello `);
const node2 = document.createTextNode(`world!`);
const body = document.body;
div.appendChild(node1);
div.appendChild(node2);
body.appendChild(div);
// hello world!
</script>
<div>hello world!</div>
<script>
// 传两个参数没什么意思的 因为script标签是插入到body的最后面才是最好的
// 或者插入到head中 然后在script代码中使用onload就好了
function loadScript(url, element) {
let script = document.createElement(`script`);
script.src = url;
element.appendChild(script);
}
loadScript('./67-动态脚本.js', document.body);
</script>
调用MutationObserver构造函数并传入一个回调函数来创建
Observer()方法,两个参数,要观察变化的DOM节点,一个MutationObserver对象(MutationObserver对象是一个键值对形式配置选项的字典,用于控制观察哪些方面的变化)
每个回调都会收到一个MUtationObserver实例的数组,表示哪里发生了变化,发生了什么变化
MutationObserver实例的属性:
| target | 被修改影响的节点 |
| — | — |
| type | 字符串,变化的类型 |
| oldValue | 变化之前的值 |
| attributeName | 被修改属性的名字 |
| attributeNamespace | 被修改属性的名字 |
| addedNodes | 变化中添加的节点,默认为空NodeList |
| removeNodes | 变化中删除的节点,默认为空NodeList |
| previousSibling | 变化节点的前一个同胞节点 |
| nextSibling | 变化节点的后一个同胞节点 |
disconnect()方法:提前终止执行回调,并且也会抛弃已经加入任务队列要异步执行的回调
多次调用observer()方法,可以复用一个MutationObserver对象观察多个不同的目标节点
MutationObserverInit对象用于控制对目标节点的观察范围,观察者可以观察的事件包括属性变化,文本变化和子节点变化
MutationserverInit对象的属性:
| subtree | 布尔值,是否观察目标节点的子树 |
| — | — |
| attributes | 布尔值,是否观察目标节点的属性变化 |
| atributeFilter | 字符串数组,要观察哪些属性变化 |
| attributeOldValue | 布尔值,是否记录变化之前的属性值 |
| charactertData | 布尔值,修改字符数据是否触发变化事件 |
| characterDataOldValue | 布尔值,是否记录变化之前的字符数据 |
| childList | 布尔值,修改目标节点的子节点是否触发变化事件 |
具体用例:
<body>
<div>
<p>MutationObserverp>
<p>MutationObserverp>
div>
<script>
// 实例化MutationObserver对象
let observer = new MutationObserver((mutationRecords) => {
console.log(mutationRecords);
})
const div = document.querySelector(`div`);
const p = document.querySelectorAll(`p`)[0];
const text = p.firstChild;
observer.observe(div, {
attributes: true,
childList: true
});
observer.observe(text, {
characterData: true
});
// 添加属性
div.setAttribute(`foo`, `bar`);
// 增添节点
let p1 = p.cloneNode(true);
div.appendChild(p1);
// 改变文本
text.textContent = `delete`;
// (3) [MutationRecord, MutationRecord, MutationRecord]
script>
body>
let parentElement = document.getElementById(`parent`);
// firstElementChild 指向第一个element类型的子元素
let currentChildElement = document.parentElement.firstElementChild;
// 若没有子元素 firstElementChild返回null 退出循环
while (currentChildElement) {
// 这就是元素节点 做相应处理
processChild(currentChildElement);
if (currentChildElement === parentElement.lastElementChild) {
break;
} else {
currentChildElement = currentChildElement.nextElementSibling;
}
}
<body>
<div class="content">
<p>today is a beautiful dayp>
<ul>
<li>play a gameli>
<li>have a sleepli>
<li>make a studyli>
ul>
<button>插入button>
div>
<script>
const content = document.querySelector(`.content`);
const btn = document.querySelector(`button`);
btn.addEventListener('click', () => {
content.innerHTML = `tomorrow is a beautiful day
`;
})
script>
body>
![image.png](https://img-blog.csdnimg.cn/img_convert/432d32700aaa7486570255cc5ab08553.png#clientId=u4050800a-b296-4&crop=0&crop=0.031&crop=1&crop=1&from=paste&height=161&id=u46ca2441&margin=[object Object]&name=image.png&originHeight=161&originWidth=214&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4366&status=done&style=none&taskId=uf9ab9aa5-3277-498a-8f3a-1a3f0b9358f&title=&width=214)![image.png](https://img-blog.csdnimg.cn/img_convert/7aef6273e9621e470e2a209a2bd82fed.png#clientId=u4050800a-b296-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=154&id=uff92c1bf&margin=[object Object]&name=image.png&originHeight=154&originWidth=229&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2052&status=done&style=none&taskId=uf0ab5fb2-9496-47e1-ad04-bd7ca683462&title=&width=229)
<body>
<ul>ul>
<script>
const ul = document.querySelector(`ul`);
const values = [1, 2, 3, 4, 5];
let itemsHtml = ``;
for (const value of values) {
itemsHtml += `${value}`;
}
ul.innerHTML = itemsHtml;
script>
body>
![偏移尺寸.png](https://img-blog.csdnimg.cn/img_convert/f2b9f188f52f2bf7ed0d8984fad686ef.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u08f50add&margin=[object Object]&name=偏移尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=262045&status=done&style=none&taskId=u65fe5e7d-643d-4565-8c3d-10b047aefc5&title=)
![客户端尺寸.png](https://img-blog.csdnimg.cn/img_convert/0066b2da19d68f1ccba07924ca3e1d94.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=ue9ef6819&margin=[object Object]&name=客户端尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=414750&status=done&style=none&taskId=u7789e14f-f37b-45b2-af37-c353648ae29&title=)
![滚动尺寸.png](https://img-blog.csdnimg.cn/img_convert/46792ea4165572bbc12fd2342e5508bb.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=uf47727b5&margin=[object Object]&name=滚动尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=427437&status=done&style=none&taskId=u0064e2db-0a3c-406c-a862-37797d81ea3&title=)
![元素尺寸.png](https://img-blog.csdnimg.cn/img_convert/0cc55dd3b61f86e75166b689446fb553.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=udf91eaa7&margin=[object Object]&name=元素尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=189138&status=done&style=none&taskId=ubac1ba90-d709-47f9-971d-aa2344fb592&title=)
<body>
<div class="div1">
<p><b>Hellob> world!p>
<ul>
<li>List item 1li>
<li>List item 2li>
<li>List item 3li>
ul>
div>
<script>
const div = document.querySelector(`div`);
// 设置过滤器 仅过滤li标签1
const filter = function(node) {
return node.tagName.toLowerCase() == `li` ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
// 创建实例 指定数值代码为SHOW_ELEMENT 过滤器filter
const iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
let node = iterator.nextNode();
while (node != null) {
// 打印标签名
console.log(node.tagName);
node = iterator.nextNode();
}
script>
body>
<body>
<p id="p1"><b>hellob>world!p>
<script>
let p1 = document.getElementById(`p1`),
helloNode = p1.firstChild.firstChild,
worldNode = p1.lastChild,
// 实例化一个范围
range = document.createRange();
// 使range包含helloNode
range.selectNode(helloNode);
const span = document.createElement(`span`);
span.style.backgroundColor = `yellow`;
// 提取出范围的内容
// 在原始文档中范围之前所在的位置插入给定的节点
// 将范围对应文档片段的内容添加给定节点
range.surroundContents(span);
console.log(p1.innerHTML);
// helloworld!
script>
body>
![image.png](https://img-blog.csdnimg.cn/img_convert/cfe71562fa2670e79429af8b14166d57.png#clientId=u2ff13313-ac9a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=36&id=u6c66b62f&margin=[object Object]&name=image.png&originHeight=36&originWidth=110&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1059&status=done&style=none&taskId=u1c4d4784-7ff5-4c32-b891-56d84c6f49e&title=&width=110)
<body>
<button>clickbutton>
<script>
const btn = document.querySelector(`button`);
btn.onclick = function() {
console.log(window.event);
};
btn.addEventListener(`click`, (event) => {
console.log(event.preventDefault);
console.log(event.stopImmediatePropagation);
console.log(event.stopPropagation);
});
script>
body>
<body>
<style>
#myMenu {
position: absolute;
visibility: hidden;
border: 1px solid rgba(0, 0, 0, .3);
}
#myMenu li {
list-style: none;
}
style>
<div id="myDiv">right click or ctrl+click me to get a custom context menu.div>
<ul id="myMenu">
<li>
<a href="https://www.baidu.com/">百度a>
li>
<li>
<a href="https://www.bilibili.com/">b站a>
li>
<li>
<a href="https://juejin.cn/">掘金a>
li>
<li>
<a href="https://developer.mozilla.org/zh-CN/">mdna>
li>
ul>
<script>
window.addEventListener(`load`, (event) => {
const div = document.querySelector(`#myDiv`);
// 给指定元素注册contextmenu事件 这是本来的菜单事件
div.addEventListener(`contextmenu`, (event) => {
// 取消默认的菜单事件冒泡
event.preventDefault();
// 显示自定义的菜单
const menu = document.querySelector(`#myMenu`);
// 使菜单跟随鼠标
menu.style.left = `${event.clientX}px`;
menu.style.top = `${event.clientY}px`;
menu.style.visibility = `visible`;
});
// 给文档注册点击事件 使左键单击时自定义菜单消失
document.addEventListener(`click`, (event) => {
document.querySelector(`#myMenu`).style.visibility = `hidden`;
});
});
script>
body>