一、基础
文档:https://developer.mozilla.org/zh-CN/
教程:https://wangdoc.com/javascript/t
(一) 简单数据类型
1.数据类型
1. Number 数字型
- [1]. Infinity 和 -Infinity表示无穷大和无穷小
//无穷大 //Number.MAX_VALUE获取最大值 console.log(Number.MAX_VALUE *2); //无穷小 console.log(Number.MIN_VALUE *2);
- [2]. NaN(Not a number), 代表一个非数字
- [3]. isNaN(var),判断变量是数字返回false,不是数字返回true。
2. String 字符串型
- [1]. 单引号和双引号是没有区别的。
- [2]. js区分大小写
- [3]. \n,换行(newline),
\t,缩进(tab)
\b,空格(blank) - [4].字符串拼接符 +:数值表示相加,字符表示相连(字符串 +任何类型 = 拼接之后的新字符串)
3. Boolean 布尔型
- [1]. true为1,false为0
- [2]. 数字型:0、NaN为假,-1为真,非0非NaN即为真
- [3]. 字符串型:非空为真,空为假
- [4]. null为假
- [5]. undefined为假
4. undefined 未定义数据类型
//结果为字符串: 'undefinedstring'
console.log(undefined + 'string');
//结果为:NaN
console.log(undefined + 1);
//结果为:NaN
console.log(undefined + true);
5. null 空值
2.判断数据的类型
- typeof 获取变量的数据类型
- 能够识别所有值类型(null特殊)
- 识别函数
- 判断是否是引用类型(不可再细分)
null -> object
array -> object
object -> objectvar name = 'xiaoming'; console.log(typeof name);
- 在浏览器中可以根据颜色判断
3.数据类型转换
1. 转换为字符串
-
[1]. toString()
var num = 1; num.toString();
-
[2]. String() 强制转换
var num = 1; String(num);
-
[3]. 加号拼接字符串
var num = 1; console.log('' + num);
2. 转换为数值型
-
[1]. parseInt(string) 将string类型转换为整数数值型
//结果为3 console.log(parseInt('3.14')); //结果为120 console.log(parseInt('120px')); //结果为NaN console.log(parseInt('a123'))
-
[2]. parseFloat(string) 将string类型转换为浮点数数值型
-
[3]. Number(string) 将string类型强制转换为数值型
-
[4]. 隐式转换
//结果为数字12,这里可以用 * - / console.log('12' - 0);
3. 转换为布尔型
- [1]. Boolean()函数
- [2]. 代表空、否定定的值都会转为false,如:‘’,0,NaN,null,undefined,其余的为true
//false console.log( Boolean(0));
(二) 数组
1. 新建一个数组
//1.利用new创建一个空数组
var arr = new Array();
arr[1,2,3,4];
//1.1.利用new创建有两个空的数组元素的数组
var arr11 = new Array(2);
//1.2.利用new创建有两个数组元素2和3的数组
var arr12 = new Array(2, 3);
//2.也可以直接利用数组字面量创建数组
var arr2 = [1,2,3,4];
//3关联型数组
var arr31 = new Array();
arr31['a'] = 1;
arr31['b'] = 2;
//4.二维数组
var a = [[1,2,3],[2,3,4]];
2. 获取、修改数组长度
- [1].length属性是可读写的
//获取数组长度 arr.length; //修改数组长度 arr.length = 5;
(三) 简单数据类型和复杂类型
1. 简单数据类型(基本数据类型、值类型)
- [1]. 值类型:存储时变量中存储的是值本身。
string,number,boolen,undefined,null,symbol - [2]. 存放在栈里面。
- [3]. 值传递
2. 复杂类型(引用类型)
- [1]. 引用类型:存储时变量中存储的仅仅是地址。
通过new关键字创建的对象,如Object、Array、Date、Function、RegExp - [2]. 存放在堆里面。
- [3]. 引用传递
(四) 函数
函数的注意事项
- [1]. 函数名一般是动词。
- [2]. 形参的默认值为undefined
- [3]. 实参个数多于形参时,多于的实参会被忽略。
实参个数少于形参时,多余的形参定义为undefined。 - [4]. 函数中尽量返回结果,而不应该输出结果。
- [5]. return只能返回一个值。如果用逗号隔开多个值,以最后一个为准。
- [6]. 如果函数没有return则返回undefined。
- [7]. 函数是有作用域的
- [8] 函数对象连接到 Function.prototype (该原型对象本身连接到 Object.prototype)
1. 函数调用模式
当一个函数并非一个对象的属性(函数是对象属性时,被称为方法)时,那么它就是被当作一个函数来调用。当一个函数被执行时,this被绑定到全局对象。(这是语言设计上的一个错误。倘若语言设计正确,那么内部函数被调用时,this应该仍然绑定到外部函数的this变量。)
var myObject = {name: '张三'};
myObject.double = function () {
console.log(this); //myObject
var helper = function () {
console.log(this); //window
}
helper();
}
myObject.double()
2. arguments的使用
-
[1]. 当我们不确定有多少参数传递的时候,可以用arguments来获取。arguments对象中存储了传递的所有实参。
function fn(){ console.log(arguments); } fn('a', 'b', 'c');
-
[2]. arguments展示形式是一个伪数组(其实是一个对象),因此可以进行遍历。伪数组具有以下特点:
- 具有length属性
- 按索引方式存储数据
- 没有有数组的push,pop等方法
-
[3]. arguments是函数内置的对象。
-
[4].arguments转换为真正的数组
var args = Array.prototype.slice.call(arguments);
3.函数的声明方式
-
[1]. 利用函数关键字自定义函数 (命名函数)
function 函数名([形参]){ ... }
-
[2]. 函数表达式 (匿名函数)
var 变量名 = function([形参]){ ... }
-
[3]. 利用Function 构造函数
Function 构造函数创建一个新的 Function 对象。直接调用此构造函数可用动态创建函数,但会遇到和 eval 类似的的安全问题和(相对较小的)性能问题。然而,与 eval 不同的是,Function 创建的函数只能在全局作用域中运行。
https://www.cnblogs.com/jtjds/articles/5795859.htmlnew Function('参数1', '参数2', '函数体')
4.立即执行函数(IIFE)
-
函数不需要调用,立即执行。也可以传递参数。
-
最大的作用就是创建了一个独立的作用域,里边的所有变量都是局部变量,完全避免了污染全局变量。
-
[1]. 写法一
(function([形参]){ ... })([实参]);
-
[2]. 写法二
(function([形参]){ ... }([实参]));
-
[3].写法三
var f = function f(){ return 1}();
(五) 作用域
1. 全局作用域
-
[1]. 范围:整个script标签或者是一个单独的js文件
-
[2]. 全局变量的作用域是全局的,在函数内部也可以使用。
-
[3]. 在函数内部没有声明直接赋值的变量也属于全局变量。
var num1 = 5;//属于全局变量 function fun(){ var num2 = 10; //属于局部变量 num3 = 20; //属于全局变量 } console.log(num2); //结果为“num2 is not defined” console.log(num3); //结果为20
2. 局部作用域(函数作用域)
- [1]. 范围:函数内部。
3. 作用域链
- [1]. 作用域链:一般情况下,变量在 创建 这个变量 的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
- [2]. 变量获取值采取就近原则
var num = 5; function fun(){ var num = 10; //2.funSon函数在fun函数的作用域中找到变量num,假设没有找到,会继续去fun函数所在的作用域中寻找,这就是作用域链 function funSon(){ //结果为10 console.log(num);//1.funSon函数在本作用域中找不到变量num,就会向上层作用域找变量num } funSon(); } fun();
4. 赋值和作用域
1. 赋值的作用域
具体结合预解析来分析
f1();
console.log(c); //结果为:9
console.log(b); //结果为:9
console.log(a); //结果为:"a is not defined"
function f1(){
//相当于var a = 9; b = 9; c = 9;
//b 和 c 直接赋值没有var 声明,是全局变量
var a = b = c = 9;
console.log(a); //结果为:9
console.log(b); //结果为:9
console.log(c); //结果为:9
}
(六) 预解析
JavaScript解析器在运行JavaScript代码的时候分为两步:预解析和代码执行。
1. 第一步:预解析
- [1]. 预解析: js引擎会把js里边所有的var 和 function提升到当前作用域的最前面。
- [2]. 预解析分为:变量预解析(变量提升)和函数预解析(函数提升)
- ①变量提升:就是把所有使用var声明的变量提升到当前作用域最前面,不提升赋值操作。
- ②函数提升:就是把所有的函数声明提升到当前作用域的最前面。
2.第二步:代码执行
- [1]. 代码执行,按照代码书写的顺序从上往下执行。
3.代码
-
[1].代码1
/*相当于执行: var num2; console.log(num2); num2 = 2; */ console.log(num2);//结果:undefined var num2 = 2; /*相当于 function fn(){ console.log('fn'); } fn(); */ fn(); //结果:fn function fn(){ console.log('fn'); } /*相当于执行: var fn1; fn1(); fn1= function(){ ... } */ fn1(); //结果"fn1 is not a function" var fn1 = function(){ console.log("fn1"); }
-
[2].代码2
/*相当于 var num; function f1(){ var num; console.log(num); num = 20; } num = 10; f1(); */ var num = 10; f1(); function f1(){ console.log(num); //结果为:undefined var num = 20; }
(七) 对象
- 一般来说,函数是单独声明,调用的方式为
函数名()
,方法在对象里边,调用的方式为对象.方法()
,但是区分并不严格。 - JavaScript中的对象分为3种:自定义对象、内置对象、浏览器对象。
- 每一个对象都连接到一个原型对象,并且它从中继承属性。所有通过对象字面量创建的对象都连接到 Object.protorype,他是JavaScript中的标配对象。
1. 自定义对象的创建和实例化
创建对象有三种方式: 利用字面量创建、利用构造函数创建、利用Object.create()创建
[1]. 利用字面量创建对象
//创建一个对象
var obj = {
name:'张三',
age:18,
say:function(){
console.log('hi~');
}
};
//使用对象属性
console.log(obj.name);
console.log(obj['age']);
//使用对象方法
obj.say();
[2]. 利用new Object创建对象(属于利用字面量创建的范畴)
var obj = new Object();
obj.name = '张三';
obj.age = 18;
obj.say = function(){
console.log("hi~");
}
//或
var obj2 = new Object({
name: '张三',
age: 18,
say:function(){
console.log("hi~");
}
})
[3]. 利用 构造函数 创建对象
-
构造函数:
是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。当构造函数没有形参时 var zs = new People; 等同于 var zs = new People();
如果函数前面带上 new 来调用,背地里将会创建一个连接到该函数的prototype成员的新对象,同时 this 被绑定到那个新对象上。
-
语法格式
规定构造函名首字母要大写//定义一个构造函数 function 构造函数名(){ this.属性名 = 值; this.方法名 = function(){ ... } } //新建一个对象 new 构造函数名();
-
代码
function People(name, age){ this.name = name; this.age = age; this.say = function(talk){ var str = this.name + 'say:' + talk; return str; } } var zs = new People('张三', 12); console.log(zs.name); //结果:张三 console.log(zs.age); //结果:12 console.log(zs.say('hello world!')); //结果:张三say:hello world!
-
this指向
1). 构造函数是个函数,构造函数的this指向创建的(实例)对象。
2). 原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就 是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。
[4]. 利用Object.create()创建对象
-
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
-
Object.create()方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。
-
新对象修改继承的属性时,并不会影响原对象的属性,且不会再受原对象的影响。
var obj = { name: '王五' } var newObj = Object.create(obj); //以obj为原型对象创建newObj对象 console.log(newObj.name);//王五 obj.name = '李四' console.log(newObj .name); //李四 newObj.name = '张三'; //不会影响obj的name属性,这时再改变obj.name不会影响到newObj.name console.log(newObj.name);//张三 console.log(obj.name);//李四
2. new 运算符
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new
https://juejin.im/post/5cb05965f265da03b8584038
-
new是来做继承的而不是所谓的创建对象!!
-
new 关键字会进行如下的操作:
-
1). 创建一个空的简单JavaScript对象(即{});
-
2). 将构造函数的prototype(原型)对象赋值给新对象的_proto_(隐式原型)
-
3). 将构造函数的作用域赋值给新对象 (也就是this指向新对象)
-
4). 如果该函数没有返回对象,则返回this。
//简单实现new function myNew(){ //obj其实等同于var obj = new Object();所以obj是Object的一个实例 var obj = {}; var Constructor = [].shift.call(arguments); // 获取arguments第一个参数:构造函数 obj.__proto__ = Constructor.prototype; // 将构造函数的原型链赋给obj // 使用apply改变构造函数this指向,指向obj对象,其后,obj就可以访问到构造函数中的属性以及原型上的属性和方法了 var result = Constructor.apply(obj, arguments) /* 1. 当构造函数返回引用类型时,构造里面的属性不能使用,只能使用返回的对象; 2. 当构造函数返回基本类型时,和没有返回值的情况相同,构造函数不受影响。 */ return typeof result === 'object' ? result : obj }
function Person(name, age) { this.name = name; this.age = age; this.gender = '男'; } Person.prototype.nation = '汉' Person.prototype.say = function () { console.log(`My name is ${this.age}`) } var person = myNew(Person, '小名', 25); console.log(person.name); console.log(person.gender); console.log(person.nation); person.say();
-
3.内置对象
[1]. Math
- Math.round() 四舍五入
- 其他数字都是四舍五入,但是 .5比较特殊,它往大的取。
Math.round(1.5); //2 Math.round(-1.5); //-1
-
Math.abs() 获取绝对值
Math.abs('-1'); //1
-
Math.random() 获取随机数,从0到1(不包含1)
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random//得到一个两数之间的随机整数,包括两个数在内 return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
[2]. Date
- Date是一个构造函数,必须使用new来调用创建日期对象。
- 没有参数返回系统的当前时间,参数可以是数字型 2020,10, 01或者是 字符串型’2019-10-01 08:08:12’。
var date1 = new Date();//返回当前时间 var date2 = new Date(2019,10, 01);//Fri Nov 01 2019 00:00:00 GMT+0800 (中国标准时间) var date3 = new Date('2019-10-01 16:08:12');//Tue Oct 01 2019 16:08:12 GMT+0800 (中国标准时间)
- date.getMonth() 获取月份(0-11)
- date. getDate() 获取当天日期
- date.getDay() 获取星期几(周日0到周六6)
- date.valueOf() 和date.getTime() 获取Date总的毫秒数(时间戳)
简单写法 :var date = +new Date();
H5新增的方法:Date.now();
[3]. Array
-
instanceof 运算符,检测是否为数组
console.log(arr instanceof Array);
-
Array.isArray(arr) 检测是否为数组
console.log(Array.isArray(arr));
-
arr.push() 在数组末尾添加一个或多个数组元素。返回新数组的长度。
arr.push(arr1)
-
arr.unshift (arr1) 在数组的开头添加一个或者多个数组元素。返回新数组的长度。
-
arr.pop() 删除数组的最后一个元素。返回被删除的元素。
-
arr.shift() 删除数组的第一个元素。返回被删除的元素。
-
arr.reverse() 反转数组。
-
arr.sort() 数组排序
arr.sort(function(a,b){ return a - b;//升序排列 return b - a;//降序排列 });
-
arr.indexOf(searchElement) 返回要查找的数组元素索引号
只返回第一个满足条件的索引号。
如果元素不存在,则返回-1。 -
arr.lastIndexOf(searchElement) 返回最后一个要查找的数组元素的索引号。
如果元素不存在返回-1。 -
arr.toString() 将数组转换为字符串,用“,”分割
-
arr.join(分隔符) 将数组转换为字符串,用分隔符分割
-
arr.splice(index,howmany,item1,…,itemX) 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。
[4]. String
字符串所有的方法,都不会改变字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。比如var str = ‘side’; str = ‘rety’;是新开辟rety的空间后将变量str的指针指向了rety。
- str.indexOf(searchValue [, fromIndex]) 根据字符返回位置
返回第一个找到的字符位置。
找不到返回-1。 - str.lastIndexOf(searchValue[, fromIndex]) 最后一次出现的索引。
- str.charAt(index) 返回指定位置的字符。
index默认为0。
如果index超出字符串长度则返回空字符串。 - str.charCodeAt(index) 获取指定位置处字符的ASCII码
- str[index] 获取指定位置的字符(H5,IE8+)
- str.concat(str1, str2,…) 拼接字符串
- str.substring(indexStart[, indexEnd]) 返回字符串的一部分,不包括indexEnd,
- str.substr(start,length) 抽取从 start 下标开始的length长度的字符。
- str.slice(start, end) 提取某个字符串的一部分,并以新的字符串返回被提取的部分。不包括end
- str.replace(substr, newSubStr) 返回替换后的新字符串
- str.split(“分隔符”) 使用指定的分隔符字符串将一个String对象分割成子字符串数组
4. typeof 和 instanceof
- [1]. typeof 判断数据类型
- 用于判断数据类型,返回值为6个字符串,分别为string、Boolean、number、function、object、undefined。
- typeof在判断null、array、object以及函数实例(new + 函数)时,得到的都是object。这使得在判断这些数据类型的时候,得不到真实的数据类型。
- [2]. instanceof 判断该对象是谁的实例
- instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
5. 对象通过引用来传递
对象通过引用来传递。它们永远不会被复制:
var obj = {name: '123'};
function asdf(obj){
obj.name = 456;
}
asdf(obj);
console.log(obj);//{name: 456}
二、 Web APIs
(一) DOM
文档对象模型(Document Object Model),是W3C组织推荐的处理可扩展标记语言(HTML或XML)的标准编程接口。
1. DOM树
-
文档:一个页面就是一个文档,DOM中使用document表示。
-
元素:页面中的所有标签都是元素,DOM中使用element表示
-
节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示。
- Document:整个文档树的顶层节点
- DocumentType:doctype标签(比如<!DOCTYPE html>)
- Element:网页的各种HTML标签(比如<body>、<a>等)
- Attr:网页元素的属性(比如class=“right”)
- Text:标签之间或标签包含的文本
- Comment:注释
- DocumentFragment:文档的片段
-
DOM把以上内容都看做是对象。
2. DOM事件级别
-
0级:是在dom元素上提供相关事件类型属性,js程序可以通过这些特定类型的属性注册事件处理程序。
特性:一个元素同种类型的事件只能注册一个事件处理程序。// 可以通过内联 和元素节点的相关属性注册事件处理程序 <div class="box" onclick="alert('box被点击了')" >x</div> document.getElementsByClassName("box")[0].onclick=function(){ console.log("hello box"); }
-
2级:在DOM元素上提供了一个通用的方法,为调用元素注册指定类型的事件处理函数,并可指定事件传播方式。大部分浏览器:addeventListener(event,listener,useCaptrue) IE11以下: attachEvent(event,listener),解除事件分别是:removeEventListerner 和 detachEvent 实现。
优点,可以给元素添加多个事件处理程序,这些事件处理程序按照他们的顺序执行。
-
3级:DOM3级事件处理跟DOM2级事件处理方法一样,就是增加了许多事件类型,包括鼠标事件,键盘事件。同时DOM3级事件也允许使用者自定义一些事件。
UI事件,当用户与页面上的元素交互时触发,如:load、scroll
焦点事件,当元素获得或失去焦点时触发,如:blur、focus
鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
文本事件,当在文档中输入文本时触发,如:textInput
键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
(二) 获取元素
1.获取元素的基础方法
- [1]. document.getElementById(id)
- [2]. document.getElementsByName(name)
- [3]. document.getElementsByTagName(‘标签名’)
获取某个元素里边的标签:
element.getElementsByTagName(‘标签名’)var nav = document.getElementById('nav'); nav.getElementsByTagName('li');
2.H5新增的获取元素的方法
- [1]. document.getElementsByClassName(‘类名’)
- [2]. document.querySelector(‘选择器’)
- 返回指定选择器的第一个元素对象。
- id选择器:#id
- class选择器:.class
- tag选择器:标签名
- [3]. document.querySelectorAll(‘选择器’)
- 返回指定选择器的所有元素对象
3. 获取特殊元素
- [1]. document.body 获取body元素,返回body元素对象
- [2]. document.documentElement 获取html元素,返回html元素对象
(三) 操作元素
1. 改变元素内容
-
[1]. element.innerText 设置或获取标签所包含的文本信息(从标签起始位置到终止位置的内容,去除HTML标签,但不包括自身),
- 非标准
- 空格和换行也会去掉
-
[2]. element.innerHTML 设置或获取标签所包含的HTML+文本信息(从标签起始位置到终止位置全部内容,包括HTML标签,但不包括自身),
- W3C标准
- 保留空格和换行
-
[3]. element.outerHTML设置或获取标签自身及其所包含的HTML+文本信息(包括自身)
<div id="testdiv"><p>Text in DIV</p></div> <script> var div = document.getElementById("testdiv"); console.log(div.innerText); console.log(div.innerHTML); console.log(div.outerHTML); </script>
2. 修改元素属性 / 自定义属性
- [1]. element.属性 = 值
获取、设置元素内置属性,有src、href、id、alt、title等。var div = document.getElememtById("img"); div.src = 'images/a.png';
- [2]. element.getAttribute(属性).
可以获取内置属性,也可以获取用户自定义的属性,主要是获取自定义的属性值。 - [3]. element.setAttribute(属性, 值).
主要是设置自定义的属性的值。 - [4]. element.removeAttribute(属性)
移除属性。 - [5]. H5自定义属性 (IE11+)
- H5规定自定义属性以data-开头作为属性名并且赋值。
- 可以使用[3][4]来获取设置自定义属性。
- H5新增 element.dataset.自定义属性 或者 element.dataset[自定义属性] 来设置获取自定义属性,但必须是data-开头的自定义属性。
- element.dataset 里边包含所有data-开头的自定义属性。
<div class="oldClass" id="box" data-bianhao="1" data-list-name="hezi"></div> <script> var box = document.getElementsByTagName("div")[0]; console.log(box.dataset); console.log(box.dataset.listName);//hezi console.log(box.dataset['bianhao']);//1 </script>
3. 操作 表单 元素属性
- [1]. element.属性 = 值
属性有type、value、checked、selected、disabled等。
4. 修改样式属性
-
[1]. 行内样式操作的读写
element.style.样式名称 -
[2]. 类名样式操作的读写
element.className用新的样式替换老的样式
var box = document.getElementById("box"); box.className = newclass;
保留老样式的同时,添加新的样式
var box = document.getElementById("box"); var oldclass = box.className; //获取老样式 element.className = oldClass + " newClass" ; //中间有个空格
这里需要注意的是,className属性,不能以新增加的方法写入class属性,必须获取老属性,然后合成字符串形式再写入!
-
[3]. 注意
- js里面的样式采取驼峰命名法。比如 fontSize、backgroundColor。
- js修改style样式操作,产生的是行内样式,css权重比较高。
- class是保留字,因此使用了className来操作元素类名属性
- className 会直接更改元素的类名,会覆盖原先的类名。
5. 获取元素宽高
-
[1]. Element.style.width/height (只能获取内连样式的宽和高): 计算不包含border,不包含padding
-
[2]. Element.currentStyle.width/height (ie支持): 计算不包含border,不包含padding
-
[3]. window.getComputedStyle(Element).width/height(ie9+, fireFox,chrome)
方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值。 私有的CSS属性值可以通过对象提供的API或通过简单地使用CSS属性名称进行索引来访问。
计算不包含border,不包含padding -
[4]. Element.getBoundingClientRect().width/height
方法返回元素的大小及其相对于视口的位置。
计算包含border,包含padding -
[5]. Element.offsetWidth / offsetHeight: 计算包含border,包含padding
-
[6]. Element.clientWidth / clientHeight: 计算不包含border,包括padding
<style> html *{ padding:0; margin: 0; } #elem-container { position: absolute; left: 100px; top: 200px; height: 100px; width: 200px; border:5px solid #000; padding: 5px; } </style> <div id="elem-container">dummy</div> <div id="elem-container2" style="height: 100px;width: 200px;transform: translate(100px, 100px);border:5px solid green;padding:5px;">dummy2</div> <div id="elem-container3" style="height: 100px;width: 200px;transform: translate(100px, 100px);border:5px solid green;">dummy3</div> <div id="elem-container4" style="height: 100px;width: 200px;transform: translate(100px, 100px);padding:5px;">dummy4</div> <script> console.log('---------基础宽度为200px----------'); var dom = document.querySelector("#elem-container"); console.log(dom.style.width); //'' console.log(window.getComputedStyle(dom).width);//200px console.log(dom.getBoundingClientRect().width);//220 console.log(dom.offsetWidth);//220 console.log(dom.clientWidth);//210 console.log('---------border + padding----------'); var dom2 = document.querySelector("#elem-container2"); console.log(dom2.style.width);//200px console.log(window.getComputedStyle(dom2).width);//200px console.log(dom2.getBoundingClientRect().width);//220 console.log(dom2.offsetWidth);//220 console.log(dom2.clientWidth);//210 console.log('---------border----------'); var dom3 = document.querySelector("#elem-container3"); console.log(dom3.style.width);//200px console.log(window.getComputedStyle(dom3).width);//200px console.log(dom3.getBoundingClientRect().width);//210 console.log(dom3.offsetWidth);//210 console.log(dom3.clientWidth);//200 console.log('---------padding----------'); var dom4 = document.querySelector("#elem-container4"); console.log(dom4.style.width);//200px console.log(window.getComputedStyle(dom4).width);//200px console.log(dom4.getBoundingClientRect().width);//210 console.log(dom4.offsetWidth);//210 console.log(dom4.clientWidth);//210 console.log('-----------ie-------------') console.log(dom.currentStyle.width); //200px console.log(dom2.currentStyle.width);//200px console.log(dom3.currentStyle.width);//200px console.log(dom4.currentStyle.width);//200px </script>
(四) 节点操作
节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
节点和元素节点是两个不同的意思。节点包含元素节点、文本节点
1. 节点层级
[1]. 父子节点
- 1). node.parentNode 获取父级节点,找不到返回null
- 2). parentNode.childNodes 获取子节点的集合
- 返回值里面包含了所有子节点,包括元素节点,文本节点(换行属于文本节点)等。
- 如果只想要获得里面的元素节点,则需要处理。所以我们一般不提倡使用childNodes。
- 3). parentNode.children 获取子元素节点的集合(非标准)
- 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回。
- 已经得到各个浏览器的支持,可以放心使用 。
- 4). parentNode.firstChild 返回第一个子节点,找不到返回null。
- 5). parentNode.lastChild 返回最后一个子节点,找不到返回null。
- 6). parentNode.firstElementChild 返回第一个子元素节点
- 7). parentNode.lastElementChild 返回最后一个子元素节点
- 6)、7)需要IE9+
- 可以使用 parentNode.children来保持兼容性
var ul = document.getElementsByTagName("ul")[0]; var first = ul.children[0]; //获取第一个 var last = ul.children[ul.children.length - 1]; //获取最后一个 console.log(first); console.log(last);
[2]. 兄弟节点
- 1). node.nextSibling 下一个兄弟节点,找不到返回null
- 2). node.previousSibling 上一个兄弟节点。
- 3). node.nextElementSibling 下一个兄弟元素节点 IE9+
- 4). node.previousElementSibling 上一个兄弟元素节点 IE9+
body 元素的节点类型 :https://www.w3school.com.cn/jsref/prop_node_nodetype.asp//要在IE9-以下使用: function getNextElementSibling(element){ var el = element; while(el = el.nextSibling){ if(el.nodeType ===1){ return el; } } return null; }
2. 创建节点
-
[1]. document.createElement(‘tagName’) 动态创建元素节点
创建多个元素效率低一点点,但是结构更清晰。//创建一个li标签,并在li标签中插入内容 var li = document.createElement('li'); li.innerText = "asdf"; console.log(li);
-
[2]. element.innerHTML 设置或返回标签之间的 HTML
var box = document.getElementById("box"); box.innerHTML = "<p>asdf</p>";
创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
//这样创建多个元素的效率会很低 var str = ''; for (var i = 0; i < 1000; i++) { document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>'; } //使用数组的形式创建多个元素的效率最高,比createElement() 也高 var array = []; for (var i = 0; i < 1000; i++) { array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>'); } document.body.innerHTML = array.join('');
-
[3]. document.write(‘tagName’)
直接将内容写入页面的内容流,但是文档流执行完毕时调用它,会导致页面的全部重绘。
正常的使用是没有问题的。
该方法可向文档写入 HTML 表达式或 JavaScript 代码。//当页面加载完毕后,再调用该方法,会直接清空body中的其他元素,只剩下<p>dsf</d> window.onload = function(){ document.write("<p>dsf</p>"); }
3. 添加子节点
- [1]. node.appendChild() 在父节点的子节点后面添加一个兄弟节点。
- [2]. node.insertBefore(child, 指定元素) 将一个节点添加到父节点的指定子节点前面。
4. 删除子节点
- [1]. node.removeChild(child) 删除DOM的一个子节点,返回删除的节点。
5. 复制节点
- [1]. node.cloneNode() 返回调用该方法的节点的一个副本。
- 如果括号里的参数为空或者为false,则为浅克隆,即只克隆复制节点本身,不可隆里面的子节点。
- 括号里为true,则是深克隆,会复制节点本身及里面所有的子节点。
6. 判断两个节点是否相等
Node.isEqualNode() 方法可以判断两个节点是否相等。当两个节点的类型相同,定义特征(defining characteristics)相同(对元素来说,即 id,孩子节点的数量等等),属性一致等,这两个节点就是相等的。一些具体的数据指出:多数时候的比较是根据节点的类型来的。
<div>This is the first element.</div>
<div>This is the first element.</div>
<script>
let divList = document.getElementsByTagName("div");
console.log(divList[0] === divList[1]); //false
console.log(divList[0].isEqualNode(divList[1])); //true
</script>
(五) 事件高级
https://www.w3school.com.cn/js/jsref_events.asp
1. 注册事件(绑定事件)
-
[1]. 传统方式
特点:注册事件的唯一性。即同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数。- 利用on开头的事件 onclick
- <button onclick = “alert(‘hi~’)”></button>
- btn.onclick = function(){ … }
-
[2] addEventListener方法监听注册方式 (w3c推荐)
- eventTarget.addEventListener(type, listener [, useCapture])
- IE9之前的IE不支持此方法,可以使用attachEvent()代替(不提倡使用)。
- 该方法将指定的监听器注册到eventTarget(目标对象)上,当该对像触发事件时,就会执行事件处理函数。
- 重复的事件不会被覆盖。
- type:事件类型字符串,比如click、mouseover,注意这里不带on。
- listener:事件处理函数,事件发生时,会调用该监听函数。
- useCapture:指定事件是否在捕获或冒泡阶段执行。true 事件句柄在捕获阶段执行, false(默认)事件句柄在冒泡阶段执行。
-
[3] attachEvent方法监听注册方式 (不提倡使用)
- attachEvent(eventNameWithOn, callback)
- 在 IE9- 可以使用
- eventNameWithOn:事件类型字符串,比如onclick,onmouseover,这里要带on。
- callback:事件处理函数,当目标触发事件时回调函数被调用。
-
[4] 注册事件兼容性解决方案
function addEventListener(element, eventName, fn){ if(element.addEventListener){ element.addEventListener(eventName, fn); }else if(element.attachEvent){ element.attachEvent('on' + eventName, fn); }else{ //相当于element.onclick = fn; element['on' + eventName] = fn; } }
2. 删除事件(解绑事件)
-
[1]. 对传统方式绑定事件的解绑
- box.onclick = null;
- 第一次实现该事件,第二次解绑
box.onclick = function(){ alert('adsf'); box.onclick = null; }
-
[2]. 对addEventListener方式绑定事件的解绑
- eventTarget.removeEventLIstener(type, listener[, useCapture]);
- 在addEventListener绑定事件时,listener 不能使用匿名函数。
function fn(){ alert('adsf'); } box.addEventListener("click", fn); //解绑 box.removeEventListener("click", fn);
- 第一次实现该事件,第二次解绑
box.addEventListener("click", fn); function fn(){ alert('adsf'); //解绑 box.removeEventListener("click", fn); }
-
[3] 对attachEvent方式绑定事件的解绑
-
eventTarget.detachEvent(type, listener);
-
listenner不能使用匿名函数。
-
第一次实现该事件,第二次解绑
box.attachEvent("onclick", fn); function fn(){ alert('adsf'); box.detachEvent("click", fn); }
-
-
[4] 解绑事件兼容性解决方案
function removeEventListener(element, eventName, fn){ if(element.removeEventListener){ element.removeEventListener(eventName, fn); }else if(element.datachEvent){ element.detachEvent('on' + eventName, fn); }else{ element['on' + eventName] = null; } }
3. DOM事件流
事件流:描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
比如给一个div注册了点击事件:
DOM事件流分为3个阶段:
①捕获阶段
②当前目标阶段
③冒泡阶段
注意:
- js代码中只能执行捕获或者冒泡其中的一个阶段。
- onclick和attachEvent只能得到冒泡阶段。
- addEventListener(type, listener[, useCapture])第三个参数如果以是true,表示事件捕获阶段调用事件处理程序;如果是false(默认),表示事件冒泡阶段调用事件处理程序。
- 实际开发中我们很少使用事件捕获,我们更关注事件冒泡
- 有些事件时没有冒泡的,比如onblue、onfocus、onmouseenter、onmouseleave。
- DOM捕获具体流程: window》document》html》body》… 》事件触发元素,冒泡就是反过来
4. 事件对象
-
[1]. 事件对象
-
event对象代表时间的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。
-
事件对象是我们事件的一系列相关数据的集合,跟事件相关的。
-
事件对象可以自己命名,如event、evt、e
-
IE678需要使用window.event
-
兼容性写法
div.onclick = function(e){ e = e || window.event; console.log(e); }
-
-
[2]. 事件对象常见的属性和方法
-
e.target和this的区别
e.target返回的是触发事件的对象(元素),this返回的是绑定事件的对象(元素)。
如:当在父元素上绑定一个click事件,点击其子元素,e.target返回的是子元素,this返回的是父元素。属性/方法 说明 e.target 返回触发事件的对象 标准 e.currentTarget 返回指向事件绑定的元素 标准 e.srcElement 返回触发事件的对象 非标准IE678 e.type 返回事件的类型,比如click,mouseover 不带on e.cancelBubble 阻止冒泡 非标准 IE678 e.returnValue 阻止默认事件(默认行为) 非标准 ie678 比如不让链接跳转 e.preventDefault() 阻止默认事件(默认行为) 标准 return false 阻止默认事件(默认行为),其后的代码不执行,而且只限于传统的注册方式 e.stopPropagation() 阻止冒泡 标准 e.stopImmediatePropagation() 阻止监听同一事件的其他事件监听器被调用。注意和stopPropagation的区别
5. 事件委托(事件代理、事件委派)
-
[1]. 原理
不是每个子节点单独设置事件监听器,而是将事件监听器设置在其父节点上,然后利用冒泡原理硬性设置每个子节点。var ul = document.querySelector("ul"); ul.addEventListener('click', function(e){ //alert(this.innerText); alert(e.target.innerText); });
-
[2]. 优点
1.减少事件注册,节省内存。
2.简化了dom节点更新时,相应事件的更新。 -
[3]. 缺点
-
事件委托基于冒泡,对于不冒泡的事件不支持。
-
层级过多,冒泡过程中,可能会被某层阻止掉。
-
理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在-table上代理td,而不是在document上代理td。
-
把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。
6. 鼠标的位置
- [1]. event.clientX和event.clientY 相对于可视区的X、Y坐标
- [2]. event.pageX和event.pageY 相对于文档页面的X、Y坐标
- [3]. event.screenX和event.screenY 相对于电脑屏幕的X、Y坐标
7. 键盘事件
- keyup和keydown事件的e.keyCode不区分字母大小写,a和A都是65
- keypress 属性在用户(在键盘上)按键时触发。
- keypress事件的e.keyCode区分大小写,a返回97,A返回65
- keypress主要用来捕获数字(注意:包括Shift+数字的符号)、字母(注意:包括大小写)、小键盘等 除了 F1-12、SHIFT、Alt、Ctrl、Insert、Home、PgUp、Delete、End、PgDn、ScrollLock、Pause、NumLock、{菜单键}、{开始键}和方向键外的ANSI字符
- keydown 和keyup 通常可以捕获键盘除了PrScrn所有按键(这里不讨论特殊键盘的特殊键)
- keydown 和keypress在文本框里面的特点:他们两个事件触发的时候,文字还没有落入文本框中。
8. mouseenter和mouseover的区别
- 当鼠标移动到元素上是就会触发mouseenter事件
- mouseover鼠标经过自身盒子会触发,经过子盒子也会触发。mouseenter只会在经过自身盒子时触发。
- 之所以这样是因为mouseenter不会冒泡。
- 跟mouseenter搭配鼠标离开mouseleave同样不会冒泡。
9. 自定义事件
-
[1]. Event()
event = new Event(typeArg[, eventInit]);
typeArg是DOMString 类型,表示所创建事件的名称。
EventInit 类型的字典,接受以下字段:
“bubbles”,可选,Boolean类型,默认值为 false,表示该事件是否冒泡。
“cancelable”,可选,Boolean类型,默认值为 false, 表示该事件能否被取消。
“composed”,可选,Boolean类型,默认值为 false,指示事件是否会在影子DOM根节点之外触发侦听器。 -
[2]. customEvent()
event = new CustomEvent(typeArg[, customEventInit]);
typeArg: 一个表示 event 名字的字符串
customEventInit:一个字典类型参数,有如下字段
“detail”, 可选的默认值是 null 的任意类型数据,是一个与 event 相关的值
bubbles 一个布尔值,表示该事件能否冒泡。 来自 EventInit。注意:测试chrome默认为不冒泡。
cancelable 一个布尔值,表示该事件是否可以取消。var obj = document.querySelector("#box"); // 添加一个适当的事件监听器 obj.addEventListener("cat", function (e) { console.log(e.detail) }) // 创建并分发事件 var event = new CustomEvent("cat", { "detail": { //自定义一个参数 "hazcheeseburger": true } }) obj.dispatchEvent(event)
-
[3].Event()和customEvent()的最大区别就是后者可以带参数
-
[4].dispatchEvent()
向一个指定的事件目标派发一个事件, 并以合适的顺序同步调用目标元素相关的事件处理函数。标准事件处理规则(包括事件捕获和可选的冒泡过程)同样适用于通过手动的使用dispatchEvent()方法派发的事件。
(六) BOM
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window。
BOM的顶级对象是window。
1. window对象常见事件
[1]. 窗口加载事件
- window.onload是窗口(页面) 加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等)就调用的处理函数。
- window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准。
- 如果使用addEventListener则没有覆盖的情况。
window.onload = function(){} 或者 window.addEventListener("load", function(){});
- DOMContentLoaded事件触发,仅当DOM加载完成,不包括样式表,图片,flash等等。 IE9+
document.addEventListener("DOMContentLoaded", function(){});
[2]. 调整窗口大小事件
-
window.onresize是调整窗口大小加载事件,当触发时就会调用处理函数。
-
window.innerWidth窗口的内部宽度。
-
window.innerHeight浏览器窗口的视口(viewport)高度(以像素为单位);如果有水平滚动条,也包括滚动条高度。
window.onresize = function(){} 或者 window.addEventListener('resize', function(){})
2. 定时器
-
1). setTimeout() 定时器,clearTimeout()停止定时器
- setTimeout()用于设置一个定时器,该定时器在定时器到期后执行调用函数。 只调用一次,就结束这个定时器。
- 延迟的时间默认为0ms。
- window经常可以省略。
timeoutID = window.setTimeout(回调函数[, 延迟的毫秒数]); window.clearTimeout(timeoutID);
-
2). setInterval()定时器,cliearInterval()停止定时器
-
setInterval()重复调用一个函数,每隔这个时间,就去调用一次回调函数。重复多次。
-
window可以省略。延迟的时间默认为0ms。
timeoutID = window.setInterVal(回调函数[, 间隔的毫秒数]); window.clearInterval(timeoutID );
-
3. JS执行机制
-
[1]. JS是单线程
单线程意味着,所有任务都需要排队,这样就会导致:如果JS执行的时间过长,就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。 -
[2]. 同步和异步
HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是,js就出现了同步和异步。- 1). 同步任务
同步任务都在主线程上执行,形成一个执行栈。 - 2). 异步任务
js的异步是通过回调函数实现的。
异步任务相关回调函数添加到任务队列中(也叫消息队列)
一般而言,异步任务有以下三种类型:
①普通事件,如click、resize等
②资源加载,如load、error等
③定时器,包括setInterval、setTimeout等
- 1). 同步任务
-
[3]. JS执行机制
- 1). 先执行执行栈中的同步任务。
- 2).遇到异步任务(回调函数)放入任务队列中。
- 3).一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
-
[4]. JS执行机制图示
由于主线程不断地重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop) -
[5]. 具体分析
//1. console.log('主线1'); //2. document.onclick = function (){console.log('click');} //3. console.log('主线2'); //4. setTimeout(function(){console.log('time')}, 3000);
- 页面加载完成后,执行到1的时候,放入到主线程执行栈中。
- 执行到2的时候,因为是回调函数,交由异步进程暂存处理,并时刻监听document的点击事件。当发生点击事件后,将点击事件触发的方法,放入到任务队列中。
- 然后执行到3的时候,放入到主线执行栈中。
- 执行到4的时候,因为是回调函数,交由异步进程暂存处理,并时刻监听倒计的结束。当倒计时结束后,将触发的方法,放入到任务队列中。
- 这时开始执行同步任务的主线程执行栈里边的1和3,控制台将打印出和‘主线1’和“主线2”。
- 如果2的点击事件在4的倒计时完成之前触发,则任务队列中先放入2的方法,然后提交给主线程执行栈,对事件进行执行,控制台将打印“click”。然后4的倒计时结束后,将触发的方法放入到任务队列,然后提交给主线程执行栈,控制台将打印“time”。
4. location对象
window对象提供了一个location属性用于获取或设置窗体的URL,并且可以用于解析URL,location属性返回的是一个对象,所以也称为location对象。
-
location对象属性
location对象属性 返回值 location.href 获取或者设置整个URL, location.host 返回主机(域名) location.protocol 返回协议 location.port 返回端口号 如果未写,返回字符串 location.pathname 返回路径 location.search 返回参数 location.hash 返回片段 #后面内容 常见于链接 锚点
在浏览器的控制台中输入location就可以得到location对象的属性。
-
location对象方法
location对象方法 返回值 location.assign() 跟href一样,可以跳转页面(也称为重定向页面) location.replace() 跳转页面,因为不记录浏览历史,所以不能后退页面 location.reload() 重新加载页面,相当于刷新按钮或者f5 如果参数为true强制刷新 ctrl+f5
5. navigator对象
navigator对象包含有关浏览器的信息。
最常用的就是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}
6. history对象
window 对象给我们提供了一个history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
history对象方法 | 作用 |
---|---|
back() | 浏览器的后退功能 |
forward() | 前进功能 |
go(参数) | 前进后退功能 参数如果是1前进1个页面,后退1个页面 |
(七) PC 端网页特效
1. 元素偏移量offset 系列
https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement
元素偏移量offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
给元素设置 transform: translate(100px, 100px);然后用offset去获取元素的偏移,获取的值是不包括transform的值。
-
1.offset 系列常用属性:
元素以带有定位的父亲元素为准,如果没有父亲元素,或者父亲元素没有定位,则以body为准。offset系列属性 作用 element.offsetParent 返回作为该元素带有定位的父级元素,如果父级没有定位则返回body element.offsetTop 返回元素相对带有定位父元素上方的偏移 element.offsetLeft 返回元素相对带有定位父元素做边框的偏移 element.offsetWidth 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 element.offsetHeight 返回自身包括padding、边框、内容区的高度,返回数值不带单位
2. offset与style的区别
3.元素可视区client系列
client系列的相关属性来获取元素可视区的相关信息,cilent系列相关属性可以动态的得到该元素的边框大小、元素大小等。
client系列属性 | 作用 |
---|---|
element.clientTop | 返回元素上边框的大小 |
element.clientLeft | 返回元素做边框的大小 |
element.clientWidth | 返回自身包括padding、内容区的宽度,不含边框,返回数值不带单位 |
element.clientHeight | 返回自身包括padding、内容区的高度,不含边框,返回数值不带单位 |
4.元素scroll系列属性
scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等。
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element.scrollWidth | 返回自身实际宽度,不含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度,不含边框,返回数值不带单位 |
- 页面被卷去的头部兼容性解决方案
- 声明了DTD(即<!DOCTYPE html>),使用document.documentElement.scrollTop
- 未声明DTD,使用document.body.scrollTop
- 页面被卷去的头部,可以通过window.pageYOffset获得,如果是被卷去的左侧window.pageXOffset IE9
function getScroll() { return { left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0, top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 }; } //使用的时候 getScroll().left
(八) 本地存储
1. 本地存储特性
- [1]. 数据存储在用户浏览器中
- [2]. 设置、读取方便、甚至页面刷新不丢失数据
- [3]. 容量较大,sessionStorage约5M、localStorage约20M
- [4].只能存储字符串,可以将对象JSON.stringIfy()编码后存储。
2. window.sessionStorage
-
[1]. 生命周期为关闭浏览器窗口。
-
[2]. 在同一个窗口(页面)下数据可以共享
(A页面通过<a>打开的同源B页面也能读取到sessionStorage共享的数据)
<a href=“./B.html” target=“_blank”>sessionStorage2</a> -
[3]. 以键值对的形式存储使用
-
[4]. 存储数据
sessionStorage.setItem(key, value);
-
[5]. 获取数据
sessionStorage.getItem(key);
-
[6]. 删除数据
sessionStorage.removeItem(key);
-
[6]. 删除所有数据
sessionStorage.clear();
2. window.localStorage
-
[1]. 生命周期永久有效,除非手动删除否则关闭页面也会存在
-
[2]. 可以多窗口(页面)共享(同一浏览器可以共享),需要同源。
-
[3]. 以键值对像是存储使用
-
[4]. 存储数据
localStorage.setItem(key, value);
-
[5]. 获取数据
localStorage.getItem(key);
-
[6]. 删除数据
localStorage.removeItem(key);
-
[6]. 删除所有数据
localStorage.clear();
3. 注意点
- [1]. 本地存储localStorage里面只能存储字符串格式,如果想要存储对象,需要将对象抓换位字符串JSON.stringify(data);
- [2]. 获取本地存储的数据,需要把里面的字符串转换为对象格式时需要使用JSON.parse()
三、 JS高级
(一) ES6中的类和对象
1. 创建类和构造函数
- ES6中没有变量提升,所以必须先定义类,才能通过类实例化对象
- 类里面的共有的属性和方法一定要加this使用
- 全局变量,在类中也可以使用。
//创建类 class Name{ //类名首字符大写 //构造函数,不需要加function,不手动添加该函数,系统也会自动生成 constructor(){ ... } //创建方法,类里面的方法不需要加function funName([arguments]){ .... } } //创建实例 var xx = new Name(); xx.funName();
2. 继承
class Father{
...
}
class Son extends Father{
...
}
3. super关键字
-
[1]. super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
-
[2]. 子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类的构造方法)
-
[3]. super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
var global = '我是全局变量'; class Father { constructor(surname) { this.surname = surname; } saySurname() { console.log('我的姓是' + this.surname); console.log(global); } } class Son extends Father { // 这样子类就继承了父类的属性和方法 constructor(surname, fristname) { super(surname); //子类重写自己的构造函数时,需要使用super调用父类的的构造函数 // 调用父类的 constructor(surname) this.fristname = fristname; } saySurname() {//子类和父类相同命名的函数,子类调用该命名的函数时,会优先选择自己的(就近原则) //想要调用父类中与子类的相同命名的的函数,使用 super.funName() super.saySurname();//调用父类的daysurname } sayFristname() { console.log("我的名字是:" + this.fristname); } } var damao = new Son('刘', "德华"); damao.saySurname(); damao.sayFristname();
4. this的指向
<button>按钮</button>
<script>
//使用全局变量that来存储类中的this
var that = null;
class But{
constructor(text) {
that = this;
this.text= text;
this.btn = document.querySelector("button");
this.btn.onclick = this.say;//点击按钮时,调用say方法,输出undefine,因为点击按钮时,say方法中的this指向的是button按钮而不是实例
}
say() {
console.log(this.text);
//可以使用
console.log(that.text);
}
}
var but= new But('我是按钮');
but.say();//输出我是按钮
</script>
(二) 构造函数和原型
https://www.cnblogs.com/huihuihero/p/11262242.html
1.构造函数
在ES6之前,对象不是基于类创建的,而是一种称为构造函数的特殊函数来定义对象和他们的特征。
[1]. 构造函数
在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数(不推荐)。
我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
[2]. 静态成员和实例成员
- 静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问。
- 实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问。 uname age sing就是实例成员。
//构造函数Star,构造函数中的属性和方法我们称为成员 function Star(uname, age) { this.uname = uname; //实例成员 this.age = age; this.sing = function () { console.log('我会唱歌'); } } Star.sex = '男'; //静态成员 console.log(Star.sex); //访问静态成员 var ldh = new Star('刘德华', 18); console.log( ldh.uname);//访问实例成员 console.log( ldh.sex);//访问静态成员,undefined
[3]. 构造函数存在的问题
每次对构造函数进行实例化后,构造函数的方法就会单独创建一个内存空间,造成内存浪费。
2.原型对象(prototype)和隐式原型(_proto_)
[1]. 构造函数原型prototype
-
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
-
构造函数通过原型分配的函数是所有对象所共享的。
-
将不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
[2]. 隐式原型__proto__
对象都会有一个属性__proto__指向构造函数的prototype原型对象。
__proto__隐式原型的意义就在于为对象的查找机制提供一个方法,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性。
//构造函数Star,构造函数中的属性和方法我们称为成员
function Star(uname, age) {
this.uname= uname;//实例成员
this.age= age;
}
//将sing方法绑定到构造函数原型上
Star.prototype.sing = function() {
console.log('我会唱歌');
}
Star.sex = '男';//静态成员
console.log(Star.sex);//访问静态成员
var ldh= new Star('刘德华', 18);
var zxy= new Star('张学友', 19);
ldh.__proto__.sing();//不使用这种方法
ldh.sing();//使用这种方法
ldh.uname;//访问实例成员
[3]. 构造函数、原型对象prototype和实例的关系
[6]. constructor 构造器
-
隐式原型(_proto_)和构造函数的原型对象(prototype)里面都有一个属性constructor,constructor称为构造函数。
-
contstructor主要用于记录该对象源自哪个构造函数,它可以让原型对象(prototype)重新指向原来的构造函数。
-
一般情况下,对象的方法都在构造函数的原型对象(prototype)中设置。如果有多个对象的方法,我们可以给原型对象(prototype)采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor 指向原来的构造函数。
function Star(uname, age) { this.uname = uname; this.age = age; } // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数 // Star.prototype.sing = function() { // console.log('我会唱歌'); // }; // Star.prototype.movie = function() { // console.log('我会演电影'); // } // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 Star.prototype = {// constructor: Star, sing: function() { console.log('我会唱歌'); }, movie: function() { console.log('我会演电影'); } } var ldh = new Star('刘德华', 18); var zxy = new Star('张学友', 19); console.log(Star.prototype); console.log(ldh.__proto__); console.log(Star.prototype.constructor); console.log(ldh.__proto__.constructor);
[7]. 构造函数和原型对象中this指向
- 在构造函数中,里面this指向的是实例对象
- 原型对象函数里面的this指向的也是实例对象
[8]. 扩展数组内置对象
注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {} ,只能是Array.prototype.xxx = function(){} 的方式
//通过原型对象,为数组扩展自定义方法
Array.prototype.sum = function(){
var sum = 0;
for(var i = 0; i < this.length; i++){
sum += this[i];
}
return sum;
}
var arr = [1, 2, 3];
console.log(arr.sum());
3.继承
ES6之前并没有给我们提供extends继承。但是可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
call方法
调用这个函数,并且修改函数运行时的this指向。
fun.call(thisArg, arg1, arg2, ...)
- thisArg:当前调用函数this的指向对象
- arg1,arg2:传递的其他参数
利用call()函数,实现继承
//父类构造函数
function Father(uname, age) {
this.uname = uname;
this.age = age;
}
//父类的静态属性
Father.count = 300;
//父类的静态方法
Father.prototype.eat = function () {
console.log(this);
console.log('我要吃饭');
}
//父类的静态属性 describe
Father.describe = function () {
console.log(this);
console.log('描述')
}
//父类的静态方法 describe
Father.prototype.describe = function () {
console.log(this);
console.log('描述2')
}
//子类构造函数
function Son(uname, age) {
//子类继承父类的实例属性
Father.call(this, uname, age);
}
Son.prototype.study = function () {
console.log(this);
console.log('我要学习');
}
//子类继承父类在构造函数上的方法,
//方式一:new Father()返回一个对象,该方法子类会有父类的实例方法,不推荐
//Son.prototype = new Father();
//方式二:Son 原型对象上的 study方法将会丢失
//Son.prototype = Object.create(Father.prototype);
//方式三: 可以保留 Son 原型对象上的 study方法 ES6
Object.setPrototypeOf(Son.prototype, Father.prototype);
//将子类的原型对象指向Son
Son.prototype.constructor = Son;
//子类继承父类的静态属性
//方式一:属性在子类中复制了一遍
// for (const key in Father) {
// if (Object.hasOwnProperty.call(Father, key)) {
// Son[key] = Father[key];
// }
// }
//方式二:属性在子类中复制了一遍
// Object.keys(Father).forEach(function(key){
// Son[key] = Father[key];
// });
//方式三: 速度慢,但是节省了空间
// Son.__proto__ = Father;
//方式四: ES6
Object.setPrototypeOf(Son, Father);
Son.prototype.exam = function () {
console.log(this);
console.log('我要考试');
}
var son = new Son('张三', 14);
console.log(son.uname);
console.log(Son.count);
Son.describe(); //描述
son.eat();
son.study();
son.describe();
son.exam();
4.ES5中新增的方法
[1]. 数组
迭代(遍历)方法:forEach()、map()、filter()、some()、every();
[2].字符串方法
trim() 方法会从一个字符串的两端删除空白字符
[3].对象方法
Object.keys() 方法返回一个所有元素为字符串的数组。
Object.defineProperty() 定义新属性或修改原有的属性。
(三) 宏队列与微队列
- 宏队列:用来保存带执行的宏任务(回调),比如:定时器回调/DOM事件回调/ajax回调。
- 微队列:用来保存待执行的微任务(回调),比如:promise的回调/MutationObserver的回调。
- js执行时会区别这两个队列
- [1]. js引擎首先必须先执行所有的初始化同步任务代码
- [2]. 每次准备去除第一个宏任务执行前,都要将所有的微任务一个一个取出来执行。
//-------------------------------------------- setTimeout(() => { Promise.resolve(3).then(value => { console.log('Promise onResolved3()', value); }) console.log('timeout callback1()'); }) //-------------------------------------------- setTimeout(() => { console.log('timeout callback2()'); }) //-------------------------------------------- Promise.resolve(1).then(value => { console.log('Promise onResolved1()', value); }) //-------------------------------------------- Promise.resolve(2).then(value => { console.log('Promise onResolved2()', value); }) //-------------------------------------------- new Promise((resolve, reject) => { console.log(155555); resolve(2); }).then(value => { console.log(value); }); //-------------------------------------------- console.log(123);
四、函数进阶
(一). 函数的定义和调用
- [1]. 利用Function 构造函数
- new Function(‘参数1’, ‘参数2’, ‘函数体’)
- 这种方式执行效率低,不方便书写,因此较少应用
- 所有函数都是Function的实例(对象)
- 函数也属于对象(万物皆对象)
var f= new Function('a', 'b', 'console.log(a+b)'); f(1, 2);
(二).改变函数内部this 指向
1. call方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
-
调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this指向。
-
如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
//接受的是一个参数列表 fun.call(thisArg, arg1, arg2, ...)
-
thisArg
可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。 -
返回值
使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。 -
在下面的例子中,我们调用了 display 方法,但并没有传递它的第一个参数。如果没有传递第一个参数,this 的值将会被绑定为全局对象。
//非严格模式下 var sData = 'Wisen'; function display() { console.log('sData value is %s ', this.sData); } display.call(); // sData value is Wisen
2. apply方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
-
apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。
fun.apply(thisArg[, argsArray]) //thisArg:在fun函数运行时指定的this值 //argsArray:参数数组 //返回值就是函数的返回值,因为他就是调用函数
-
主要应用 比如我们可以利用apply借助数学内置对象求最大值。
var arr = [1, 5, 2]; var max = Math.max.apply(Math, arr); console.log(max);
3. bind方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
- bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
fun.bind(thisArg, arg1, arg2); //thisArg:在fun函数运行时指定的this值 //arg1, arg2:传递的其他参数 //返回由指定的this值和初始化参数改造的原函数拷贝
- 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind方法
- bind 返回的是一个新的函数,你必须调用它才会被执行。
- 在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。
var btn = document.querySelector('button'); btn.onclick = function (){ this.disabled = true; setTimeout(function(){ //如果不加bind方法,此时的this指向window this.disabled = false; //加上bind方法,this指向btn console.log(this); }.bind(this), 3000);//这里的this可以使用btn来代替 }
4. call apply bind方法总结
https://www.runoob.com/w3cnote/js-call-apply-bind.html
- call和apply的区别就是传递参数的不同,call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
- call 经常做继承
- apply 经常跟数组有关系
- bind 应用于不调用函数,但是还想改变this的指向。
(三). 函数/对象中的this
https://juejin.im/post/5aa1eb056fb9a028b77a66fd#heading-1
-
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)。
function test(name) { console.log(name) console.log(this) } //等同于test.call(undefined, 'Tom') test('Jerry') //调用函数
-
一般情况下this的最终指向的是那个调用它的对象。
-
全局作用域或者普通函数中this指向全局对象window。(注意定时器里面的this指向window)。
function fn(){ console.log(this);}//this指向window fn();//全称为window.fn();
-
对象中的方法中的this指向的是对象
var name = '张三' var obj = { objName: this.name,//指向全局 sayHi:function(){ console.log(this);//指向obj } } console.log(obj.objName) obj.sayHi();
-
构造函数中this指向构造函数的实例。
function Fun(){ console.log(this); //this指向的是fun实例对象 } var fun = new Fun();
(四). js严格模式
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
1. 严格模式
- JavaScript 除了提供正常模式外,还提供了严格模式(strictmode)。ES5 的严格模式是采用具有限制性JavaScript 变体的一种方式,即在严格的条件下运行JS 代码。
- 严格模式在IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
2.开启严格模式
-
1).为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’;)。<script> "use strict"; //下面的js代码就会按照严格模式执行代码 console.log("这是严格模式。"); </script>
-
2).为脚函数开启严格模式
要给某个函数开启严格模式,需要把“use strict”; (或’use strict’; ) 声明放在函数体所有语句之前。function fn(){ "use strict"; return "这是严格模式。"; }
(五). 高阶函数
一个函数就可以接收另一个函数作为参数或者返回值为一个函数,这种函数就称之为高阶函数。
function fn(callback){
callback&&callback();
}
fn(function(){alert('hi');})
function fn(){
return function(){}
}
fn();
(六). 闭包
-
闭包(closure)指能够读取其他函数内部变量的函数。。
-
简单理解为:“定义在一个函数内部的函数”。
-
闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
-
注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
//一个简单的闭包 function createIncrementor(start) { return function () { return start++; }; } var inc = createIncrementor(5); inc() // 5 inc() // 6 inc() // 7
-
利用闭包获取li的索引号
var lis = document.querySelector('ul').querySelectorAll('li'); for(var i = 0; i < lis.length; i++){ (function (arg){ lis[arg].onclick = function(){ console.log(arg); } })(i); }
(七). 递归
递归函数:递归函数就是直接或间接地调用自身的一种函数。
-
求N的阶乘
function fn(n){ if(n == 1){ return 1; } return n * fn(n - 1); } console.lo(fn(3));
(八).深拷贝和浅拷贝
浅拷贝只是将变量的内存地址进行传递,深拷贝是在内存中开辟了一个新的空间。
var o = {
id: 1,
name: 'andy',
msg:{
tell:"说点什么"
}
}
var obj ={};
obj = o; //浅拷贝
obj.id = 2;
console.log(o.id); //输出2
var obj2 = {};
for(var k in o){
obj2[k] = o[k]; //深拷贝
}
obj2.id = 3;
console.log( obj2.id); //输出3
console.log( o.id); //输出2
(九).JSON和对象
[1].js对象(数组) 转为json对象(数组):
JSON.stringify(obj/arr)
[1].json对象(数组)转为js对象(数组):
JSON.parse(json)
(十).抛出和捕获错误
function something() {
if (Date.now() % 2 !== 1) {
console.log("是奇数");
} else {
throw new Error("抛出错误");
}
}
try {
something();
} catch (error) {
console.log(error.message);
}
五、正则表达式
(一) 创建正则表达式
1.利用RegExp对象的构造函数创建
var 变量名 = new RegExp(/表达式/);
2.通过字面量创建
var rg = /表达式/;
2.测试正则表达式
test正则对象方法,用于检查字符串是否符合改规则,该对象会返回true或false,其参数是测试字符串。
regexObj.test(str);
3.正则表达式中的特殊字符
-
[1]. 边界符 : ^ $
-
[2]. 字符类:[ ],只要包含其中一个即可(多选一)
[ - ],从范围内选择一个
[^],中括号里边的^表示取反 -
[3]. 量词符: * + ? {n} {n,} {n,m}
六、ES6新增语法
1. 作用域
https://www.cnblogs.com/pandawind/p/9798436.html
- 函数作用域: 由于函数执行,会产生作用域,这块作用域内存放着当前函数内部执行的代码中的变量,函数。
- 块级作用域
- 特点: 块级作用域声明的变量只会在当前作用域及其以下的作用域可以访问的到
- 存在于: {} 中,也被称为块中。if语句和for语句里面的{ }也属于块作用域。
- 在ES5中,只全局作用域和函数作用域。这会导致函数作用域覆盖了全局作用域;亦或者循环中的变量泄露为全局变量。
2.let
ES6中新增的用于声明变量的关键字
-
1). let声明的变量只能在所处于的块级有效
-
注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域(var属于函数作用域)。
if(true){ let temp = 1; if(true){ let temp2 = 2; console.log(temp);//结果为1 console.log(temp2);//结果为2 } console.log(temp);//结果为1 console.log(temp2);//结果为temp2 is not defined }
-
2).可以防止循环变量变成全局变量
for (let i = 0; i < 2; i++){ console.log(i); // 0 1 } console.log(i); //i is not defined
-
3).使用let关键字声明的变量没有变量提升(即不可以先使用后声明)
-
4).使用let关键字声明变量后,在同一作用域下不能重复声明同一个变量
let name = '张三'; //Uncaught SyntaxError: Identifier 'name' has already been declared let name = '李四';
-
5).使用let关键字具有暂时性死区
let temp = 10; if(true){ console.log(temp); //10 }
let temp = 10; if(true){ console.log(temp); //Cannot access 'temp' before initialization let temp = 20; }
-
实现点击按钮实现该按钮的序号
<button>按钮1</button> <button>按钮2</button> <button>按钮3</button> <button>按钮4</button> <script> var btns = document.querySelectorAll("button"); //1.无论点击哪个按钮都会输出:第4个按钮被点击 /*for(var i = 0; i < btns.length; i++){ btns[i].addEventListener('click', function(){ console.log("第"+ i + '个按钮被点击'); }); }*/ //2.利用闭包实现想要的效果,因为闭包就是一个函数,而函数是有作用域的 /*for(var i = 0; i < btns.length; i++){ (function(m){ btns[m].addEventListener('click', function(){ console.log("第"+ m + '个按钮被点击'); }) })(i); }*/ //3.利用let for(let i = 0; i < btns.length; i++){ btns[i].addEventListener('click', function(){ console.log("第"+ i + '个按钮被点击'); }); } </script>
3.const
-
在ES6开发中,优先使用const,只有需要改变某一个标识符的时候才使用let。
-
1).作用:声明一个只读的常量(数组和对象不属于常量),常量就是值(内存地址)不能变化的量
-
2).具有块级作用域
if(true){ const a = 10; if(true){ const a = 20; console.log(a);//输出20 } console.log(a);//输出10 } consle.log(a);//consle is not defined
-
3).声明常量时必须要赋初始值
-
4).常量赋值后,值不能修改(内存地址不可更改)。
const arr = [1, 2]; arr[1] = 100; console.log(arr[1]); // 100 arr = 100; //Assignment to constant variable.
-
5).常量指向一个对象时,指向的对象不能修改,但是对象中的属性可以修改
const obj = {name:'张三'}; obj.name = '李四'; console.log(obj.name);
3. 解构赋值
ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构。
-
数组解构
let [a, b, c, e] = [1, 2, 3];//变量和值一一对应 console.log(a); //1 console.log(b); //2 console.log(c); //3 console.log(e); //undefined
-
对象解构
let person = {name:"zhangsan", age:20}; let {name, age} = person; console.log(name); //张三 console.log(age); //20
4. 箭头函数
-
ES6中新增的定义函数的方式
var fn = ()=>{ console.log(123); } fn();
-
函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号。
const sum = (n1, n2)=> n1 + n2; const result = sum(10, 20); console.log(result); //30
-
箭头函数中,如果形参只有一个,则形参外的小括号也可以省略。
箭头函数中的this
https://juejin.im/post/5aa1eb056fb9a028b77a66fd#heading-1
1). 箭头函数的特性一:默认绑定外层this
-
[1]. 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。(箭头函数没有this关键字,箭头函数中的this,引用的就是最近的作用域中的this)
/*因为箭头函数默认不会使用自己的this,而是会和外层的this保持一致, 最外层的this就是obj对象。*/ /*函数obj.a没有使用箭头函数,因为它的this还是obj, 而setTimeout里的函数使用了箭头函数, 所以它会和外层的this保持一致,也是obj; 如果setTimeout里的函数没有使用箭头函数, 那么它打出来的应该是window对象。*/ const obj = { a: function() { console.log(this) window.setTimeout(() => { console.log(this) }, 1000) } } obj.a.call(obj) //第一个this是obj对象,第二个this还是obj对象
function fn1(){ console.log(this); //window (()=>{ console.log(this); //window })() } fn1();
2). 箭头函数的特性二:不能用call方法修改里面的this
const obj = {
a: () => {
console.log(this)
}
}
obj.a.call('123') //打出来的结果依然是window对象
3). 多层对象嵌套里函数的this
obj.a调用后打出来的是obj对象,而obj.b.c调用后打出的是window对象而非obj,这表示多层对象嵌套里箭头函数里this是和最最外层保持一致的。
const obj = {
a: function() { console.log(this) },
b: {
c: () => {console.log(this)}
}
}
obj.a() //没有使用箭头函数打出的是obj
obj.b.c() //打出的是window对象!!
5. 剩余参数
剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
const sum = (...args)=>{
console.log(args);
};
sum(10, 20);
sum(10, 20, 30);
剩余参数和解构配合使用
let arr = ['a', 'b', 'c'];
let [s1, ...s2] = arr;
console.log(s1); //a
console.log(s2); // b, c
6. Array的扩展方法
-
扩展运算符(展开语法)
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。let arr = [1, 2, 3]; //...arr 相当于1,2,3 console.log(...arr); //输出1 2 3
-
[1].数组合并:
//方法1 let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2] console.log(...arr3); //输出1 2 3 4 5 6 //方法2 arr1.push(...arr2);
-
[2]. 将伪数组或可比案例对象转换为真正的数组
let oDivs = document.getElementsByTagName("div"); oDivs = [...oDivs];
-
[3]. 使用构造方法:Array.form()将伪数组转换为真正的数组,该方法的第二个参数,类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
var arrayLike = { "0":"1", "1":"2", "length":2 } var arr = Array.from(arrayLike, (item)=>{ return item * 2; }); console.log(arr);//返回数组[2, 4]
-
[4]. find()用于找出第一个符合条件的数组成员,如果没有返回undefind
var arr = [{ id:1, name:'张三' },{ id:2, name:"李四" }]; //找到id=2的对象 let target = arr.find((item, index)=>item.id == 2); console.log(target);
-
[5].findIndex(),用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
let arr = [1, 2, 15, 25]; let index = arr.findIndex((value, index)=>value>10); console.log(index); //2
-
[6].includes()表示某个数组是否包含给定的值,返回布尔值
let arr = [1, 2, 3]; console.log(arr.includes(2)); //true console.log(arr.includes(4)); //false
7. String的扩展方法
-
[1]. ES6新增的创建字符串额方式,使用反引号定义。
模板字符串可以解析变量。let name = `张三`; console.log(`我叫${name}`);
-
[2]. 模板字符串可以换行
let html = `<div> <span>123</span> </div>`;
-
[3]. 模板字符串中可一个调用函数。
const sayHello = function(){ return '213454'; } let greet = `${sayHello()}dsafasdf`; console.log(greet);
-
[4]. startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值let str = 'hello world!'; console.log(str.startsWith('hello')); //true console.log(str.endsWith('!')); //true
-
[5]. repeat(n) 方法表示将原字符串重复n次,返回一个新字符串
console.log('x'.repeat(3));//xxx
8. Set数据结构
set数据结构类似于数组,但是成员的值都是惟一的,没有重复的值。
-
[1]. 初始化Set, Set可以接受一个数组作为参数,用来初始化
//数组去重 var arr = [1,2,3,2]; const s2= new Set(arr); //var arr2 = [...new Set(arr2)] console.log(s2);
-
[2]. Set的size属性用于查看Set元素的个数
-
[3]. Set接受的数组,会自动将数组中重复的元素去除。
-
[4]. add(value):添加某个值,返回Set结果本身
const s = new Set(); s.add(1).add(2).add(3).add(2); console.log(s);//Set(3) {1, 2, 3}
-
[5]. delete(value):删除某个值,成功返回true,失败返回false
-
[6]. has(value):返回一个布尔值,表示该值是否为Set的成员
-
[7]. clear() 清除所有成员,没有返回值
-
[8]. Set结构的实例和数组一样,也可以使用forEach方法,用于对每个成员执行某种操作,没有返回值。
9. 对象字面量增强写法
-
对象字面量写法
const obj = {};
-
对象字面量的增强写法
const name = '张三'; const age = 16; var height = 1.88; let width = 0.5; //ES5写法 const obj = { name : name, age : age, height : height, width : width, run : function(){ console.log("obj run"); } } //ES6写法 const obj1 = { name, age, height, width, run(){ console.log("obj1 run"); } } console.log(obj); console.log(obj1);
七、JavaScript容易被忽略的点
-
JavaScript区分大小写。
-
undefined是一个特殊的值,表示“无定义”。
-
switch语句后面的表达式,与case语句后面的表示式比较运行结果时,采用的是严格相等运算符(===)。
-
break跳出当前循环或代码块,continue立即终止本轮循环,返回循环结构的头部,开始下一轮循环。
-
避免使用全局变量,全局变量要大写。
-
函数表达式末尾要使用分号。
-
字符串建议使用单引号。
-
行首以大括号开头,一律按代码块算
{foo: 'abc'} //代码块 ({foo: 'abc'})//对象 eval('{foo: 123}') // 123 eval('({foo: 123})') // {foo: 123}
-
变量提升和函数提升的次序
var f = function (){ console.log(123); }; function f() { console.log(456); } f();//123
-
函数本身的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
var a = 1; var x = function () { console.log(a); }; function f() { var a = 2; x(); } f() // 1