0. DOM事件和监听 DOM方法汇总
js的入口函数:
window.onload= function() { }
// 获取body元素
var bodyEle = document.body;
// 获取html元素
var htmlEle = document.documentElement;
JavaScript与HTML可以通过以下几种方式结合:
1. 内联方式(Inline):
将JavaScript代码直接写入HTML标签中的"onclick"、"onload"等事件属性中。
2. 内部嵌入式(Internal Embedded):
在HTML文件中使用<script>标签将JavaScript代码包含在页面内,通常放在<head>或<body>元素内。
在 `` 或者 `` 的JavaScript:
您可以在 HTML 文档中放入不限数量的脚本。
脚本可位于 HTML 的 `` 或` `部分中,或者同时存在于两个部分中。
通常的做法是把函数放入 ` `部分中,或者放在页面底部。这样就可以把它们安置到同一处位置,不会干扰页面的内容。
3. 外部链接式(External Linked):
将JavaScript代码单独保存为.js文件,在HTML文件中使用<script src="filename.js"></script>引入外部脚本。
4. 事件处理程序(Event Handlers):
通过监听用户操作或者浏览器事件来触发特定的函数执行相应的逻辑。
5. DOM操作(Document Object Model):
利用JavaScript对文档对象模型进行增删改查,实现动态修改页面内容和样式。
6. AJAX交互(Asynchronous JavaScript and XML):
利用JavaScript异步发送HTTP请求获取数据并更新网页内容,实现无需刷新页面的动态交互效果。
例子:
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 内部JS-->
<script>
alert("HelloWorld");
</script>
<!-- 外部JS-->
<script src="JS/a.js"></script>
</head>
<body>
</body>
</html>
a.js
alert("我是外部的JS文件");
单行注释: //注释内容 多行注释: / * 注释内容 * /'运行
一共8种
number :数字。 整数 / 小数 / NaN (not a number一 个不是数字的数字类型)。string:字符串。 字符串 “abc”、 "a”、 ’ abc’。boolean: true和false。null :一 个对象为空的占位符。undefined :未定义。如果一个变量没有给初始化值,则会被默认赋值为undefined。Symbol(ES新增)BigInt(ES10新增)object基本数据类型:
<script>
var a = [0, 1, 2, 3, 4, 5, 6]
var b = a //传地址
var c = a[0] //传值
console.log('b=' + b);
console.log('c=' + c);
// 改变数值
b[4] = 6
c = 7
console.log('------------------');
console.log('a=' + a);
console.log('b=' + b);
console.log('c=' + c); //c改变不影响引用类型
</script>
基本数据类型中null和undefined的区别以及应用://都是代表没有值

null表示“没有对象”,该处不应该有值
undefined表示“缺少值”,该处应该有值,但是还没有定义
转为数值也不同,null转为数值为0,undefined转为数值NaN(不是一个数字)
console.log(Number(null));//0 console.log(Number(undefined));//nan'运行
什么时候会有null:
1、作为函数的参数,表示该函数的参数不是对象
2、作为对象原型链的终点为NULL
console.log(Object.getPrototypeOf(Object.prototype));//null'运行
Object.getPrototypeOf(object)方法返回指定对象的原型(内部[[Prototype]]属性的值)。
给定对象的原型。如果没有继承属性,则返回 null 。
什么时候会出现undefined:
1、变量被声明了。但是没有赋值,就等于undefined
var a
log a //undefined
2、调用函数时,应该提供的参数没有提供,该参数就等于undefined
function fun1(a) { console.log(a); } fun1()//undefined'运行
3、对象没有赋值的属性,该属性的值为undefined
var obj = {} console.log(obj.name);'运行
4、函数没有返回值时,默认返回undefined
function fun1(a) { console.log(a); } var b = fun1(1) console.log(b);//undefined'运行
Symbol 指的是独一无二的值。每个通过 Symbol() 生成的值都是唯一的。
一个Symbol值能作为对象属性的标识符;
这是该数据类型仅有的目的。
let var_symbol = Symbol(); let other_symbol = Symbol(); console.log(var_symbol === other_symbol); // false console.log(typeof var_symbol); // symbol console.log(var_symbol.constructor === Symbol) // true'运行
那么,如何使用 Symbol 创建两个可以相等的变量呢?
let var_symbol = Symbol.for('symbol'); let other_symbol = Symbol.for('symbol'); console.log(var_symbol === other_symbol) // true'运行
Symbol.for(key) 方法会根据给定的键 key(字符串),来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数。而在其他编程语言中,可以存在不同的数字类型,例如:整数、浮点数、双精度数或大斐波数。
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。
// 超过 53 个二进制位的数值,无法保持精度 Math.pow(2, 53) === Math.pow(2, 53) + 1 // true // 超过 2 的 1024 次方的数值,无法表示 Math.pow(2, 1024) // Infinity'运行
ES2020 引入了一种新的数据类型 BigInt,来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
为了与 Number 类型进行区分,BigInt 类型的数据必须添加后缀n。
12 // 普通Number
12n // BigInt
// BigInt 的运算
1n + 2n // 3n
// 与Number 类型进行运算
1 + 1n // Uncaught TypeError
BigInt 与普通整数是两种值,它们之间并不相等。
12n === 12 // false'运行
由于 BigInt 与 Number 完全属于两种类型,并且不会进行隐式转换,所以没有办法进行混合运算。想要运算的话,必须将两种数据类型转换为同一张后,方可进行计算:
BigInt(number) // 将一个 Number 转换为 BigInt
Number(bigint) // 将一个 BigInt 转换为 Number
typeof 运算符对于 BigInt 类型的数据返回 bigint。
typeof 12n // 'bigint''运行
由于 BigInt 并不是一个构造函数,所以,不能使用 new BigInt() 的方式来构建实例
new BigInt()
// Uncaught TypeError: BigInt is not a constructor at new BigInt
另外,当你创建一个 BigInt 的时候,参数必须为整数,否则或报错
BigInt(1.2)
// Uncaught RangeError: The number 1.2 cannot be converted to a BigInt because it is not an integer
变量:一小块存储数据的内存空间
Java语言是强类型语言,而JavaScript是弱类型语言。
语法:
var 变量名 = 初始化值;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>变量</title>
<script>
//定义变量
var a = 3;
alert(a);
a="abc";
alert(a);
//定义number类型
var num = 1;
var num2 = 1.2;
var num3 = NaN;
//输出到页面上
document.write(num+"---"+typeof(num)+"
");
document.write(num2+"
");
document.write(num3+"
");
//定义stirng类型
var str = "abc";
var str2 = 'edf';
document.write(str+"
");
document.write(str2+"
");
//定义boolean
var flag = true;
document.write(flag+"
");
// 定义null
var obj = null;
var obj2 = undefined;
var obj3;
document.write(obj+"
");
document.write(obj2+"
");
document.write(obj3+"
");
</script>
</head>
<body>
</body>
</html>


Q:null为基本类型,为啥类型检测为object?
A:null为空对象的指针,为基本类型,不为引用类型。
Q:typeof NaN是什么结果?
A:number。NaN表示是否属于number类型的一种状态,是或否,不是一种确切的值。JS中number除了浮点型和整数型,还有一个特殊的存在NaN
const a = 'abc'; console.log(Number(a)); //NaN // nan为一个不确切的值范围 console.log(NaN === NaN);//false console.log(NaN !== NaN);//true'运行
object instanceof constructor
不能判断基本数据类型,能判断原型链
当typeof都为object时,使用intanceof来判断类型,查找原型链
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
prototype 返回对象类型原型的引用
不能检测null和undefined


不能检测null和undefined










找不到方法时,就去隐式原型上去找方法


Student.prototype和s1.__proto__相等

面试题
Function.prototype.a = {}; Object.prototype.b = {}; var F = function(){} var f = new F(); // f里有方法a和方法b吗? console.log(f); // 只有方法b a存在于Function原型对象中'运行


原型链查找属性 作用域链查找变量
前提场景:Teacher继承Person类,Person类包含一个Teacher类没有的drink方法



Person.prototype.__proto__ = Object.prototype;
// 系统底层会执行类似代码建立默认继承关系
检测是否为本身还是在原型上

intanceof
对象构造函数的prototype原型属性,是否出现在对象的原型链上,存在intanceof就为true


Object.prototype.__proto__; // null
Object.constructor; // ƒ Function() { [native code] }1
Function.__proto__ === Function.prototype; // { /* Function原型对象 */ }
__proto__ 即是向上查找父级原型Person.protype.__proto === Object.prototype;



instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
使用 instanceof判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false

每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type]
除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文
const an = ['Hello','An']; an.toString(); // "Hello,An" Object.prototype.toString.call(an); // "[object Array]"'运行
slice包头不包尾
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
console.log(Object.prototype.toString.call([]).slice(8, -1));//Array'运行
// 对象隐式原型等于构造函数的显式原型 console.log([].__proto__ === Array.prototype);//true'运行
console.log(Array.isArray([]));'运行
console.log([] instanceof Array)'运行
判断Array的prototype是否在对象的原型链上面
Array.prototype.isPrototypeOf([]);'运行
+ 操作中一个操作数是字符串,则进行字符串的拼接。否则进行数字加法。
从左到右依次计算
console.log(1 + 2 + 1 + 'aa' + 1 + 1);//4aa11 console.log('aa' + 1 + 2 + 1);//aa121'运行
===、==的区别
隐式转换就是自动转换,通常发生在一些数学运算中。因为 JavaScript 是一种弱类型的语言,在一个表达式中,运算符两边的类型可以不同(比如一个字符串和一个数字相加),JavaScript 解释器会在运算之前将它们的类型进行转换


JS中深拷贝和浅拷贝的区别
主要在于复制出来的新对象和原来的对象是否会互相影响,改一个,另一个也会变
浅拷贝和深拷贝都是对象或数组的复制方式,但它们的实现方式和效果有所不同。
浅拷贝:
仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变
新旧对象共享内存,修改其中一个,另一个也会受到影响
浅拷贝的引用类型被改变 会改变元数据的引用地址
浅拷贝是指只复制对象的第一层属性,如果对象的属性值是引用类型,则只复制其引用地址。
浅拷贝在复制时,只复制对象或数组本身以及第一层的属性或元素。
如果被复制的对象或数组中包含了引用类型的属性或元素,那么这些引用类型的属性或元素会共享同一个内存地址。因此,在修改其中一个对象或数组时,会影响到其他对象或数组。
示例代码:

上面代码中,使用Object.assign()方法进行浅拷贝后,修改obj2.b.c的值也会影响到原始对象obj1。
深拷贝:
在内存中开辟一块新的地址用于存放复制的对象
新旧对象不会共享内存,修改其中的一个不会影响另一个
深拷贝是指在复制时,完全复制对象或数组以及所有嵌套的属性或元素,并且新生成的对象与原始对象没有任何关联。
因此,在修改其中一个对象或数组时,不会对其他任何对象造成影响。
示例代码:

上面代码中,使用JSON.parse()和JSON.stringify()方法进行深拷贝后,修改obj2.b.c的值不会影响到原始对象obj1。
需要注意的是,在使用深拷贝时,可能存在无法复制的属性或元素,例如函数、undefined等。另外,对于循环引用的对象或数组,也需要特殊处理以避免出现死循环。
var obj1 = { name: "zs", age: 20 } var obj2 = obj1 console.log(obj1);//{name: 'zs', age: 20} obj1.age = 22 console.log(obj1);//22 console.log(obj2);//22'运行
浅拷贝是指只复制对象的第一层属性,如果对象的属性值是引用类型,则只复制其引用地址。下面是一个浅拷贝的例子:

从上面代码可以看到,使用 Object.assign() 方法进行浅拷贝后,修改了克隆对象的属性值后原始对象和克隆对象的 name 和 age 属性不同,但是它们共享同一个 hobbies 数组。这就是因为在浅拷贝中,只有第一层属性被复制了过来,而对于嵌套在第二层及以下的属性则仍然共享同一个内存地址。
如果需要深度拷贝整个嵌套结构体,需要使用深拷贝。

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

Array.from() 方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
对数组元素的浅复制,而不是对数组的浅复制
slice是浅拷贝。它会创建一个新数组,该新数组包含原始数组中的一部分元素。但是,这些元素仍然引用原始数组中的相同对象,因此如果修改原始数组中的对象,则也会反映在新数组中。
slice操作针对引用类型是浅拷贝。也就是说,在新数组中复制的对象仍然引用原始数组中相同的对象。
如果修改原始数组中的某个对象,则会反映在新数组中相应位置的对象上。
但是,对于基本数据类型(如数字、字符串等),slice操作实际上是深拷贝,因为它们被直接存储在数组中而不是通过引用传递。
console.log(Array.from('foo')); // expected output: Array ["f", "o", "o"] console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]'运行
let a = [1, 2, [1, 2, 3]]; let b = Array.from(a); console.log(a == b); // false a[2][1] = 11; a[0] = 11; console.log(a); // [ 11, 2, [ 1, 11, 3 ] ] console.log(b); // [ 1, 2, [ 1, 11, 3 ] ] console.log(a[2] == b[2]); // true'运行
JSON.stringify:将js的值(对象或者数组)转为一个JSON字符串
JSON.parse:用来解析JSON字符串,转换为Object类型
var obj1 = { name: "zs", age: 20 } var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj1); console.log(obj2); obj1.age = 30 console.log(obj1);//30 console.log(obj2);//20'运行
扩展运算符是浅拷贝。
使用扩展运算符可以将一个对象的所有属性复制到另一个对象中。但是,当对象中存在嵌套的引用类型属性时,它们仍然只会被复制引用地址而不是完整的拷贝值。

const list = { //引用对象为浅拷贝 china: { city: '成都' } } const listCopy = { ...list }; listCopy.china.city = '上海' console.log(list);//上海 console.log(listCopy);//上海'运行
需要影响引用类型
// 浅拷贝 var obj1 = { name: "zs", age: 20 } var obj2 = { ...obj1, age: 30 } console.log(obj1);//20 console.log(obj2);//30 obj2.age = 22 console.log(obj1);//20 console.log(obj2);//22'运行
仅对引用类型数据的第一层进行了拷贝,而倘若再深的层次就不会进行拷贝。
//数组的复制 第一层深拷贝 再深层次浅拷贝 var arr1 = [1,2,3,4,[5]]; var arr2 = [...arr1] arr1[0]=18; arr2[4][0]=100; console.log(arr1); console.log(arr2);'运行
或:先检测第一个字是否boolean类型,如果不是boolean类型,就强转布尔类型。第一个为真,则返回第一个数。否则返回第二个。
console.log(1 || 2);//1 console.log(0 || 2);//2'运行
与:第一个操作数为真,返回第二个操作数
第一个操作数为假,则返回第一个操作数
console.log(1 && 2);//2 console.log(0 && 2);//0 console.log(2 && 0);//0'运行
不是返回true和false,返回操作数值。
console.log(1 == '1');//true console.log(1 == {});//false console.log(true == 3);//false console.log(true == 1);//true var a = {} var b = a var c = {} console.log('a==c:' + (a == c)); //false console.log('a==b:' + (a == b)); //true console.log('b==c:' + (b == c)); //false'运行
isNaN(value)为了判断一个计算结果value或变量的值value是否为NaN
isNaN的判断过程:首先进行类型检测,如果传入的参数不是数值类型,第二步将传入的参数转为数值类型,然后再进行是否为NaN的判际NaN。



const保证并不是变量的值不能改动,而是变量指向的内存地址不能改动
// const保证并不是变量的值不能改动,而是变量指向的内存地址不能改动 const obj = { name: 'lishi', age: 20 }; console.log(obj);//{name: 'lishi', age: 20} obj.name = '李四' console.log(obj);//{name: '李四', age: 20}'运行






// 引用地址不一样 不能由此判断是否为空对象 console.log({} === {});//false console.log({} == {});//false // 使用JSON的stringify方法 var obj = {} console.log(JSON.stringify(obj) === '{}');//true // 使用Object.keys() 枚举自身属性,返回字符串数组 var obj1 = { name: '1231', age: 333 } var obj2 = {} console.log(Object.keys(obj1));//['name', 'age'] console.log(Object.keys(obj2));//[] console.log(Object.keys(obj2).length);//0'运行


// 作用一:将数组变为一个以空格分隔的序列 var arr = [1, 2, 3, 4, 5, 6] console.log(...arr);//1 2 3 4 5 6 空格分隔 // 作用二:复制 //数组的复制 var arr1 = [1, 2, 3, 4, [5]]; var arr2 = [...arr1] arr1[0] = 18; arr2[4][0] = 100; console.log(arr1); console.log(arr2);//二维深度为浅拷贝 // 对象的复制(深拷贝) var obj1 = { name: 'zs', age: 18 } var obj2 = { ...obj1 } obj1.age = 20; console.log(obj1); console.log(obj2); // 作用三:合并 // 数组的合并 var arr3 = [1, 2, 3, 4] var arr4 = [5, 6, 7] console.log([...arr3, ...arr4]); //[1, 2, 3, 4, 5, 6, 7] //对象的合并 var o1 = { name: 'lisi' } var o2 = { age: 22 } console.log({ ...o1, ...o2 });//{name: 'lisi', age: 22}'运行


DOM : document,文档对象类型,用来获取或者设置文档标签的属性S可以通过DOM获取到有哪些标签,标签有哪些属性,内容有哪些,DOM操作的对象是文档,所以DOM和浏览器没有关系,关注网页本身的内容
BOM:browser object model,浏览器对象模型,提供独立于内容而与浏览器窗口进行交互的对象
管理窗口与窗口之间的通讯,核心对象是window–> location(用F于url相关的操作)、history(用于历史相关的操作),navigator(包含了浏览器相关的信息)BOM是控制浏览器行为的api, DOM是一个页面结构的api
// 默认绑定全局this function girl() { console.log(this); } girl();//log window'运行
// 隐式绑定 var girl = { name: '小红', age: 22, detail: function () { console.log(this.name); console.log(this.age); } } girl.detail();'运行
// 硬绑定 var girlName = { name: '小红', sayName: function () { console.log(this.name); } } var girl1 = { name: '小白' } var girl2 = { name: '小黄' } girlName.sayName.call(girl1);//小白 girlName.sayName.call(girl2);//小黄'运行
// 构造函数绑定 function Lover(name) { this.name = name; this.sayName = function () { console.log(this.name); }; } var name = '小白' var xiaohong = new Lover('小红') xiaohong.sayName();//小红'运行

输出


输出


call是用来修改this指向的
每个js函数都Function对象,Function对象是构造函数,构造函数有原型对象Function.prototype,call就是原型对象属性来的
Function.prototype.myCall = function (obj, ...arr) { const newObj = obj || global; newObj.p = this; const result = newObj.p(...arr); delete newObj.p; return result; };'运行
构造函数XMLHttpRequest可创建一个xhr实例,可调用open、readyState、send方法

参见AJAX的使用

使用promise封装ajax
详细见 39.ES6使用Promise封装AJAX

// JS中arguments类数组 // 为什么函数的arguments参数是类数组而不是数组 ? 如何遍历类数组 ? // 类数组:与数组相似,但是没有数组常见的方法属性 function func() { console.log(arguments); for (let i = 0; i < arguments.length; i++) { console.log(arguments[i]); } } func(1, 2, 3, 4); console.log('------------------------------------------'); // 1.将数组的方法应用到类数组上 function fun1() { Array.prototype.forEach.call(arguments, (item) => { console.log(item); }) } fun1(1, 2, 3, 4); console.log('------------------------------------------'); //2.将类数组专为数组 使用Array.from function fun2() { const arr = Array.from(arguments) arr.push(5) console.log(arr); } fun2(1, 2, 3, 4);//[1,2,3,4] console.log('------------------------------------------'); //3.使用展开运算符 function fun3() { const arr1 = [...arguments]; console.log(arr1); arr1.forEach((item) => { console.log(item); }) } fun3(1, 2, 3, 4);//[1, 2, 3, 4]'运行
使用instanceof
//类本身指向是构造函数 类的数据类型是函数 function Person(name) { this.name = name; } const obj = new Person('李四'); // instanceof:判断构造函数的prototype是否在对象的原型链上(更准确) const obj1 = {}; console.log(obj instanceof Person);//true console.log(obj1 instanceof Person);//false //使用对象属性constructor来判断 指向该对象的构造函数 console.log(obj.__proto__.constructor); // ƒ Person(name) { // this.name = name; // }'运行
两者返回的值不一样,区别:map分配内存空间存储新数组,并且返回,forEach不会返回
Array.prototype.forEach()
foreach()返回undefined
forEach() 方法对数组的每个元素执行一次给定的函数。
const array1 = ['a', 'b', 'c']; array1.forEach(element => console.log(element)); array1.forEach( (element, index, arr) => { console.log(element);//element数组的遍历 console.log(index);//index 索引 console.log(arr);//arr数组本身 })'运行
Array.prototype.map()
map()在内存中返回一个新数组
map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
const array1 = [1, 4, 9, 16]; // pass a function to map const map1 = array1.map(x => x * 2); console.log(map1); // expected output: Array [2, 8, 18, 32]'运行
// for...of是允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值 // for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句 const array1 = ['a', 'b', 'c']; for (const element of array1) { console.log(element); } // expected output: "a" // expected output: "b" // expected output: "c" // 需要遍历的对象是类数组对象,使用数组的Array.from转为数组 var obj = { 0: 1, 1: 2, 2: 3, length: 3 } obj = Array.from(obj) console.log(obj); for (const i of obj) { console.log(i); } console.log('----------------------------------------'); // 需要遍历的对象不是类数组,需要给对象添加一个Symbol.iterator属性,指向迭代器 // iterator遍历过程 //1、创建一个指针对象,指向当前数据结构的起始位置 //2、第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员 //3、第二次调用指针对象的next方法,指针就指向数据结构的第二个成员 //4、不断调用指针对象的next方法,直到它指向数据结构的结束位置 //每一次调用next方法,都会返回数据结构的当前成员的信息,包含value和done两个属性的对象 // done:是一个布尔值 代表遍历是否结束 var person = { name: '张三', age: 12, gender: '男', height: 188 } person[Symbol.iterator] = function () { //拿到对象中所有的key值 // var key = Object.keys(person) let keys = Object.keys(this); // 定义下标值 let index = 0; console.log('keys:' + keys); return { next() { if (index < keys.length) { return { value: person[keys[index++]], done: false } } return { value: undefined, done: true } } } } for (let value of person) { console.log(value); }'运行
var arr = ['red','green', 'blue'];
for(var i = 0; i < arr.length; i++){
console.log(arrStus[i]);
}
// forEach:不会改变原数组 没有返回值
const forEachResult =
arr.forEach((element, index, arr) => {
console.log(element);
console.log(index);
console.log(arr);
});
console.log(forEachResult);//undefined
// map :不会改变原数组 具有返回值
const mapResult = arr.map(
(item, index, arr) => {
console.log(item);
console.log(index);
console.log(arr);
return item * 2
}
)
console.log('mapRES:' + mapResult);//2 4 6
filter() 方法创建给定数组一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素。
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter(word => word.length > 6); console.log(result); // expected output: Array ["exuberant", "destruction", "present"]'运行
// filter 过滤数组 返回符合条件的元素数组
const resultFilter = arr.filter(
(item) => {
return item > 1
}
)
console.log(resultFilter);//[2, 3]
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
// for ... of 返回是数组的元素 对象的属性值 不能遍历普通的对象
for (const value of arr) {
console.log(value);
}
reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被作为初始值 initialValue,迭代器将从第二个元素开始执行(索引为 1 而不是 0)。
const array1 = [1, 2, 3, 4]; // 0 + 1 + 2 + 3 + 4 const initialValue = 0; const sumWithInitial = array1.reduce( (previousValue, currentValue) => previousValue + currentValue, initialValue ); console.log(sumWithInitial); // expected output: 10'运行
// reduce 接收一个函数 作为一个累加器
// pre 计算之后返回的值
// item 当前的值
// 回调函数从数组索引为 0 的元素开始执行
let result = arr.reduce((pre, item) => {
// console.log('pre;' + pre);
// console.log('item:' + item);
return pre + item
}, 0)
console.log(result);



bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。


一个 Promise 必然处于以下几种状态之一:
待定(pending):初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled):意味着操作成功完成。
已拒绝(rejected):意味着操作失败。
特点:状态不受外界的影响,只有异步操作的结果决定当前是哪一种状态
一旦状态改变就不会再变(pending–>fufilled, pending–>rejected)
Promise是什么?:Promise是一个构造函数,用来生成Promise实例
Promise构造函数接受一个函数作为参数,函数带有两个参数,resolve,reject



将多个Promise对象 包装成一个Promise对象
Promise.all()
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个Promise实例


应用场景

多个promise包装成一个promise对象。
看某个实例先返回状态。先返回的实例传给race的回调函数。
Promise.race()
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。

成功

失败

使用场景





XMLHttpRequest()

XMLHttpRequest.open()
XMLHttpRequest.open() 方法初始化一个新创建的请求,或重新初始化一个请求。
xhrReq.open(method, url, async, user, password);

XMLHttpRequest.send()
XMLHttpRequest.send() 方法用于发送 HTTP 请求。如果是异步请求(默认为异步请求),则此方法会在请求发送后立即返回;如果是同步请求,则此方法直到响应到达后才会返回。


XMLHttpRequest.readyState

XMLHttpRequest.response

创建元素
使用 JS 可以为一个已有的元素添加一个新的子元素。
第一步:创建空元素。
var elem = document.createElement("标签名");
创建元素后,可以像使用 DOM 树中的任意元素一样,为此元素添加属性或内容。
elem.id = "xxx";
elem.innerHTML = "xxx";
注意:元素创建完成后,只是在内存中保存,并没有添加到 DOM 树。
第二步:将新创建的元素添加到 DOM 树的指定父元素下。
在父元素末尾追加:parent.appendChild(elem);
添加到某个子元素之前:parent.insertBefore(a,child);
替换某个子元素:parent.replaceChild(a,child);
JS 优化建议:尽量少的操作 DOM 树,同时添加父元素及其子元素的时候,先在内存中将子元素创建完毕并拼到父元素中,再一次性将父元素添加到页面。
实例:JavaScript DOM:Node对象、添加子节点、删除子节点
JavaScript DOM API中append和appendChild的不同点
addEventListenter(事件,回调函数,布尔值(false冒泡,true捕获)默认为false)addEventListener(type, listener, useCapture);
EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。


阻止默认行为
preventDefault()方法
e.preventDefault();
兼容ie678
e.returnValue = false
阻止事件冒泡
e.stopPropagation()
ie 678 —— e.cancelBubble

// 无函数
sort()
// 箭头函数
sort((a, b) => { /* … */ } )
// 比较函数
sort(compareFn)
// 内联比较函数
sort(function compareFn(a, b) { /* … */ })
降序
.sort(function (a, b) {
return b - a;
});
升序
.sort(function (a, b) {
return a - b;
});

//防抖和节流:限制函数执行的次数
//JS中实现函数的防抖
//通过setTimeout的方式,在一定的时间间隔内,将多次触发变成一次触发
let ipt = document.getElementById('ipt');
let btn = document.getElementById('btn');
function getValue(e) {
let val = ipt.value
console.log(val);
console.log(e);
console.log(this);
}
btn.onclick = debounce(getValue, 1000);
// 简版
function debounce(fn, delay) {
let timer = null;
return function () {
clearTimeout(timer)
timer = setTimeout(fn, delay)
}
}
function debounce(fn, delay) {
// 定义最开始定时器对象
let timer = null;
return function () {
let that = this;
if (timer) {//判断定时器是否生成 生成则清除上一个定时器
clearTimeout(timer);
}
// 判断第一次点击 立即执行
let firstClick = !timer;
if (firstClick) {
fn.apply(that, arguments)//改变this指向到btn
}
timer = setTimeout(() => {
timer = null
}, delay)
}
}

// 节流: 减少一段时间的触发频率(时间戳),控制事件发生的频率,控制在2s发生一次
let ipt = document.getElementById('ipt');
let btn = document.getElementById('btn');
btn.onclick = throttle(getValue, 2000);
function getValue() {
let val = ipt.value
console.log(val);
}
function throttle(fn, time) {
let begin = 0;//设置时间的初始值
return function () {
// 当前时间戳
let date = new Date().getTime();
let that = this;
if (date - begin > time) {
fn.apply(that, arguments)
begin = date;
}
}
}
//scroll: 每隔一秒计算一次位置信息
// 搜索框实时搜索,并且发送请求,展示下拉列表,每隔两秒发送一次请求

注意点
1:箭头函数内的this是静态的,总是指向定义时所在的对象,而不是调用时。并且this指向是不可以改变的。

2.this始终指向函数声明时所在作用域下的this的值。

3.箭头函数不能当做构造函数,也就是不可以用new命令,否则报错。

4.箭头函数不存在arguments对象,即不能使用伪数组去接收参数,可以使用rest参数代替。

使用场景


loaclStorage:永久存储在本地,适合保存在本地的数据sessionStorage:会话级的存储,敏感账号一次性登录保存数据语法
localStorage.setItem(“key”, “value”);
sessionStorage.setItem(“key”, “value”);
读取数据语法:
var lastname = localStorage.getItem(“key”);
var lastname = sessionStorage.getItem(“key”);
删除数据语法:
localStorage.removeItem(“key”);
sessionStorage.removeItem(“key”);
内存泄漏: 由于疏忽或错误造成程序未能释放已经不再使用的内存



作用域


作用域链:本层找不到 继续查找外层
原型链查找属性 作用域链查找变量



闭包是指有权访问另外一个函数作用域中的变量的函数





继承可使子类有父类的各种属性和方法



HTML网页主要有以下几种布局:
流式布局是一种响应式设计(responsive design)的布局方式,也被称为“弹性布局”或“自适应布局”。它通过设置百分比宽度、最大宽度和最小宽度等属性来实现页面元素的自适应调整,从而能够在不同设备和屏幕大小下呈现出更好的用户体验。
在使用流式布局时,需要遵循以下原则:
使用百分比定义元素宽度;
针对不同尺寸设备设置不同的样式表;
注意保证最小宽度和最大宽度之间合理的差距,以免造成排版混乱或无法满足需求。
固定宽度布局(Fixed Width Layout):
指定页面元素的宽度为固定像素值,使得页面在不同分辨率下显示效果一致。但是可能会出现在小屏幕上无法完全显示或者在大屏幕上留有过多空白区域的问题。
响应式布局(Responsive Layout):
通过CSS Media Query技术实现,根据设备屏幕宽度来自适应地调整样式,使得页面能够在不同尺寸的设备上保持良好的可读性和可用性。
响应式布局和流式布局都是为了适应不同设备屏幕尺寸而设计的,但它们有以下不同:
1. 响应式布局通过媒体查询来实现针对不同屏幕大小的样式调整,
而流式布局则是根据相对比例设置元素宽度,当窗口大小改变时会自动适应。
2. 响应式布局通常使用断点(breakpoint)来指定在不同尺寸下使用不同的样式规则,
而流式布局没有明确定义的断点。
3. 响应式布局可以在某些情况下隐藏或移除某些元素以提高用户体验。
而流式布局并不能很好地处理这种情况。
网格布局(Grid Layout):
通过CSS Grid技术实现,将页面划分成网格,并且指定每个网格中元素的位置和大小,可以实现更复杂、灵活和美观的页面布局。
flex布局
flex布局是CSS3中新增的一种布局方式,用于更加方便、快捷地实现页面元素的排列和对齐。
它通过对容器和子元素设置一系列属性来控制元素的尺寸、位置和排列方式。
使用flex布局可以实现以下优点:
1. 灵活性强:可以根据不同设备和屏幕大小动态调整布局;
2. 可读性好:代码简洁明了,易于理解和维护;
3. 响应式设计:可以自适应不同设备和屏幕大小,适应多种场景需求。
flex布局包括两个概念:
容器(flex container)和项目(flex item)。
容器是指包含要进行排列的所有元素的父级元素,而项目则是指这些被排列的子元素。
在使用flex布局时,需要对容器和项目分别设置属性来控制其展示效果。
常用的属性有:
- 容器相关属性
- display: flex; // 将容器设置为一个弹性盒子
- flex-direction: row/row-reverse/column/column-reverse; // 设置主轴方向
- justify-content: flex-start/flex-end/center/space-between/space-around; // 主轴上项目的对齐方式
- align-items: flex-start/flex-end/center/baseline/stretch; // 交叉轴上项目的对齐方式
- flex-wrap: nowrap/wrap/wrap-reverse; // 是否允许项目换行,以及换行方式
- 项目相关属性
- flex: none/1/auto; // 项目的放大比例、缩小比例和基准大小
- order: <integer>; // 控制项目的排列顺序
- align-self: auto/flex-start/flex-end/center/baseline/stretch; // 自身在交叉轴上的对齐方式
需要注意的是,在使用flex布局时需要考虑到浏览器兼容性问题,
特别是一些老旧浏览器可能不支持部分flex属性。
在前端开发中,性能调优是一个重要的主题。以下是一些常见的技术和实践,可以帮助提高前端应用程序的性能:
减少 HTTP 请求:将多个 CSS 和 JavaScript 文件合并成单个文件,并使用图像精灵(sprite)来减少页面请求次数。
压缩文件大小:压缩 HTML、CSS 和 JavaScript 文件以减小文件大小,并加快页面加载速度。
懒加载:懒加载图片和其他资源,只有当用户滚动到它们所在的位置时才加载它们。这样可以减少页面初始加载时间,并且仅在需要时加载额外内容。
避免 DOM 操作:避免频繁地更新 DOM 元素,因为这会导致浏览器重新渲染整个页面。相反,尝试使用文档片段或虚拟 DOM 等技术来进行批量操作。
缓存数据:使用本地存储或浏览器缓存等技术来缓存数据,以便在下一次访问时可以更快地检索数据。
代码优化:编写高效的代码并避免不必要的计算和循环。此外,在处理大型数据集时,请尽可能使用数组方法而不是循环迭代。
减少重绘和回流:避免频繁更改页面元素的样式和布局,因为这会导致浏览器重绘和回流。相反,请尝试使用 CSS transform 和 opacity 属性等技术来进行动画和过渡。
服务端渲染(SSR):使用服务端渲染可以提高首屏加载速度,并减少客户端资源的需求。
总之,性能调优是一个复杂的过程,需要注意各种因素。对于前端开发人员来说,理解浏览器原理、网络协议和编码最佳实践是必不可少的。
当使用new操作符创建一个对象时,会进行以下步骤:
obj.__proto__ = Person.prototype)Person.call(obj))例如:
function Person(name, age) { this.name = name; this.age = age; } const john = new Person('John', 30);'运行
在这个例子中,调用new Person('John', 30)会执行以上步骤,最后生成一个包含name和age属性的新对象,并将其赋值给变量john。
JavaScript 创建对象的方式主要有以下几种:
通过直接定义键值对来创建一个简单对象。

通过使用 new 运算符调用构造函数来创建一个对象,其中构造函数可以接收参数并在内部进行属性赋值和方法定义等操作。

Object.create() 静态方法以一个现有对象作为原型,创建一个新对象。



ES6 提供了 Class 关键字来定义类,并在类中定义属性和方法。通过使用 new 运算符调用类来创建一个新的对象。

assign

使用构造器创建

先捕获,后冒泡,捕获是先父后子,冒泡是先子后父


阻止事件流行为,我们可以用
e.stopPropagation()

将事件处理程序附加到父元素,以处理其子元素的事件;利用事件流中冒泡的机制,将点击事件委托给父元素,这种处理方案被称之为事件委托(代理)

将点击事件监听器添加到了父元素 button-container 上。当任何子元素(按钮)被点击时,事件将冒泡到父元素,然后我们检查被点击的元素是否拥有特定的类名(dynamic-button),以此来判断是否点击了按钮。如果是按钮,则可以执行相关的处理逻辑。
这种方法可以帮助你避免为每个按钮都单独添加事件监听器,从而提高了代码的效率和可维护性。
event.target
触发事件的元素,被点击的具体元素,当可以发生变化
event.currentTarget
绑定事件的元素,
如document.body.onclick = xxx,就永远是document.body