JavaScript 基础知识总结(下)
预编译
规律1:任何变量,如果未经声明就赋值,此变量是属于 window 的属性,而且不会做变量提升。(注意,无论在哪个作用域内赋值)
比如说,如果我们直接在代码里写 console.log(a),这肯定会报错的,提示找不到 a。但如果我直接写 a = 100,这就不会报错,此时,这个 a 就是 window.a。
规律2:一切声明的全局变量,全是window的属性。(注意,我说的是在全局作用域内声明的全局变量,不是说局部变量)
比如说,当我定义 var a = 200 时,这此时这个 a 就是 window.a。
由此,我们可以看出:window 代表了全局作用域(是说「代表」,没说「等于」)。
函数预编译的步骤
函数预编译,发生在函数执行的前一刻。
(1)创建AO对象。AO即 Activation Object 活跃对象,其实就是「执行期上下文」。
(2)找形参和变量声明,将形参名和变量作为 AO 的属性名,值为undefined。
(3)将实参值和形参统一,实参的值赋给形参。
(4)查找函数声明,函数名作为 AO 对象的属性名,值为整个函数体。
this 指向
根据函数的调用方式的不同,this 会指向不同的对象:
1.以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如fun();相当于window.fun();
2.以方法的形式调用时,this 指向调用方法的那个对象
3.以构造函数的形式调用时,this 指向实例对象
4.以事件绑定函数的形式调用时,this 指向绑定事件的对象
5.使用 call 和 apply 调用时,this 指向指定的那个对象
call 和 apply 和 bind
call() 方法的作用
call() 方法的作用:可以调用一个函数,与此同时,它还可以改变这个函数内部的 this 指向。
call() 方法的另一个应用:可以实现继承。之所以能实现继承,其实是利用了上面的作用。
语法:
fn1.call(想要将this指向哪里, 函数实参1, 函数实参2);
var obj1 = {
nickName: 'hello',
age: 28,
};
function fn1(a, b) {
console.log(this);
console.log(this.nickName);
console.log(a + b);
}
fn1.call(obj1, 2, 4); // 先将 this 指向 obj1,然后执行 fn1() 函数
通过 call() 实现继承:
// 给 Father 增加 name 和 age 属性
function Father(myName, myAge) {
this.name = myName;
this.age = myAge;
}
function Son(myName, myAge) {
// 【下面这一行,重要代码】
// 通过这一步,将 father 里面的 this 修改为 Son 里面的 this;另外,给 Son 加上相应的参数,让 Son 自动拥有 Father 里的属性。最终实现继承
Father.call(this, myName, myAge);
}
const son1 = new Son('test', 28);
console.log(JSON.stringify(son1));
fn1.apply(想要将this指向哪里, [函数实参1, 函数实参2]);
备注:第一个参数中,如果不需要改变 this 指向,则传 null。
到这里可以看出, call() 和 apply() 方法的作用是相同的。唯一的区别在于,apply() 里面传入的实参,必须是数组(或者伪数组)。
bind() 方法的作用
bind() 方法不会调用函数,但是可以改变函数内部的 this 指向。
把call()、apply()、bind()这三个方法做一下对比,你会发现:实际开发中, bind() 方法使用得最为频繁。如果有些函数,我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用 bind() 是最为合适的。
高阶函数
当函数 A 接收函数 B 作为参数,或者把函数 C 作为返回值输出时,我们称 函数 A 为高阶函数。
通俗来说,高阶函数是 对其他函数进行操 的函数。
高阶函数举例1:把其他函数作为参数
function fn1(a, b, callback) {
console.log(a + b);
// 执行完上面的 console.log() 语句之后,再执行下面这个 callback 函数。也就是说,这个 callback 函数是最后执行的。
callback && callback();
}
fn1(10, 20, function () {
console.log('我是最后执行的函数');
});
打印结果:
30 我是最后执行的函数
高阶函数举例2:把其他区函数作为返回值
function fn1() {
let a = 20;
return function () {
console.log(a);
};
}
const foo = fn1(); // 执行 fn1() 之后,会得到一个返回值。这个返回值是函数
foo();
闭包
我们知道,变量根据作用域的不同分为两种:全局变量和局部变量。
函数内部可以访问全局变量和局部变量。
函数外部只能访问全局变量,不能访问局部变量。
当函数执行完毕,本作用域内的局部变量会销毁。
闭包的作用:延伸变量的作用范围
我们来看看下面这段闭包的代码:
function fn1() {
let a = 20;
function fn2() {
console.log(a);
}
return fn2;
}
const foo = fn1(); // 执行 fn1() 之后,会得到一个返回值。foo 代表的就是 fn2 函数
foo();
上方代码中,foo 代表的就是整个 fn2 函数。当执行了 foo() 语句之后(相当于执行了 ),fn1 函数内就产生了闭包。
一般来说,在 fn1 函数执行完毕后,它里面的变量 a 会立即销毁。但此时由于产生了闭包,所以 fn1 函数中的变量 a 不会立即销毁,因为 fn2 函数还要继续调用变量 a。只有等所有函数把变量 a 调用完了,变量 a 才会销毁
而且,可以看出, 在执行 foo()语句之后,竟然能够打印出 20,这就完美通过闭包实现了:全局作用域成功访问到了局部作用域中的变量 a。
因此,我们可以看出,闭包的主要作用就是:延伸了变量的作用范围。
对象的创建和构建函数
对象字面量的属性名可以加引号也可以不加,建议不加。如果要使用一些特殊的名字,则必须加引号。
属性名和属性值是一组一组的键值对结构,键和值之间使用:连接,多个值对之间使用,隔开。
使用工厂方法创建的对象,使用的构造函数都是 Object。所以创建的对象都是 Object 这个类型,就导致我们无法区分出多种不同类型的对象。
方式三:利用构造函数
//利用构造函数自定义对象
var stu1 = new Student('smyh');
console.log(stu1);
stu1.sayHi();
var stu2 = new Student('vae');
console.log(stu2);
stu2.sayHi();
// 创建一个构造函数
function Student(name) {
this.name = name; //this指的是当前对象实例【重要】
this.sayHi = function () {
console.log(this.name + '厉害了');
};
}
构造函数和普通函数的区别就是调用方式的不同:普通函数是直接调用,而构造函数需要使用 new 关键字来调用。
this 的指向也有所不同:
1.以函数的形式调用时,this 永远都是 window。比如fun();相当于window.fun();
2.以方法的形式调用时,this 是调用方法的那个对象
3.以构造函数的形式调用时,this 是新创建的实例对象
对象的基本操作
获取对象中的属性
方式 1:
语法:
对象.属性名;
如果获取对象中没有的属性,不会报错而是返回undefined。
可以使用[]这种形式去操作属性
对象的属性名不强制要求遵守标识符的规范,不过我们尽量要按照标识符的规范去做。
但如果确实要使用特殊的属性名,就不能采用.的方式来操作对象的属性。比如说,123这种属性名,如果我们直接写成obj.123 = 789来操作属性,是会报错的。那怎么办呢?办法如下:
语法格式如下:(读取时,也是采用这种方式)
// 注意,括号里的属性名,必须要加引号
对象['属性名'] = 属性值;
//检查对象 obj 中是否含有name属性
console.log('name' in obj);
ES6 中,如果我们要遍历一个数组,可以这样做:
let arr1 = [2, 6, 8, 5];
for (let value of arr1) {
console.log(value);
}
Map 对象的遍历
for ... of既可以遍历数组,也可以遍历 Map 对象。
for in:遍历对象的属性
for ... in主要用于遍历对象,不建议用来遍历数组。
浅拷贝和深拷贝
浅拷贝:只拷贝最外面一层的数据;更深层次的对象,只拷贝引用。
深拷贝:拷贝多层数据;每一层级别的数据都会拷贝。
用 Object.assgin() 实现浅拷贝(推荐的方式)
上面的 for in 方法做浅拷贝过于繁琐。ES6 给我们提供了新的语法糖,通过 Object.assgin() 可以实现浅拷贝。
Object.assgin() 在日常开发中,使用得相当频繁,非掌握不可。
const myObj = {
name: 'qianguyihao',
age: 28,
}
// 【写法1】浅拷贝:把 myObj 拷贝给 obj1
const obj1 = {};
Object.assign(obj1, myObj);
// 【写法2】浅拷贝:把 myObj 拷贝给 obj2
const obj2 = Object.assign({}, myObj);
// 【写法3】浅拷贝:把 myObj 拷贝给 obj31。注意,这里的 obj31 和 obj32 其实是等价的,他们指向了同一个内存地址
const obj31 = {};
const obj32 = Object.assign(obj31, myObj);
深拷贝其实就是将浅拷贝进行递归。
用 for in 递归实现深拷贝
代码实现:
let obj1 = {
name: 'qianguyihao',
age: 28,
info: {
desc: 'hello',
},
color: ['red', 'blue', 'green'],
};
let obj2 = {};
deepCopy(obj2, obj1);
console.log(obj2);
obj1.info.desc = 'github';
console.log(obj2);
// 方法:深拷贝
function deepCopy(newObj, oldObj) {
for (let key in oldObj) {
// 获取属性值 oldObj[key]
let item = oldObj[key];
// 判断这个值是否是数组
if (item instanceof Array) {
newObj[key] = [];
deepCopy(newObj[key], item);
} else if (item instanceof Object) {
// 判断这个值是否是对象
newObj[key] = {};
deepCopy(newObj[key], item);
} else {
// 简单数据类型,直接赋值
newObj[key] = item;
}
}
}
正则表达式
(1)创建正则表达式的对象 reg。
(2)使用 reg 的test() 方法,判断指定字符串是否符合规则。
正则表达式的test()方法:【重要】
myReg.test(str); // 判断字符串 str 是否符合 指定的 myReg 这个正则表达式的规则
解释:使用test()这个方法可以用来检查一个字符串是否符合正则表达式的规则,如果符合则返回true,否则返回false。
构造函数 RegExp 中,也可以传两个参数。我们可以传递一个匹配模式作为第二个参数。这个参数可以是:
i 忽略大小写。这里的 i 指的是 ignore。
g 全局匹配模式。这里的 g 指的是 global。
代码举例:
var reg = new RegExp('A', 'i');
var str = 'qiangu';
console.log(reg.test(str)); // 打印结果:true
var reg = /A/i; // 定义正则表达式的规则:检查一个字符串中是否含有 a。忽略大小写。
var str = "qiangu";
console.log(typeof reg); // 打印结果:object
console.log(reg.test(str)); // 打印结果:true
以上两种方式的对比
方式一:使用构造函数创建时,更加灵活,因为参数中还可以传递变量。
方式二:使用字面量的方式创建,更加简单。
全局匹配模式g一般用于 exec()、match()、replace()等方法。
全局匹配模式g如果用于test()方法会有问题。因为g模式会生成一个lastindex参数来存储匹配最后一次的位置。
检查一个字符串中是否包含 a或b
写法1:
var reg = /a|b/;
解释:使用 | 表示或的意思。
写法2:
var reg = /[ab]/; // 跟上面的那行语法,是等价的
解释:这里的[]也是表示或的意思。
一些规则:
/[ab]/ 等价于 /a|b/:检查一个字符串中是否包含 a或b
/[a-z]/:检查一个字符串那种是否包含任意小写字母
/[A-Z]/:任意大写字母
/[A-z]/:任意字母
/[0-9]/:任意数字
/a[bde]c/:检查一个字符串中是否包含 abc 或 adc 或 aec
[^ ] 表示:除了
支持正则表达式的 String 对象的方法
String对象的如下方法,是支持正则表达式的:
方法描述备注split() | 将字符串拆分成数组 |
search() | 搜索字符串中是否含有指定内容,返回索引 index |
match() | 根据正则表达式,从一个字符串中将符合条件的内容提取出来 |
replace() | 将字符串中的指定内容,替换为新的内容并返回
DOM
ECMAScript:JavaScript的语法标准。包括变量、表达式、运算符、函数、if语句、for语句等。
DOM:文档对象模型(Document object Model),操作网页上的元素的API。比如让盒子移动、变色、轮播图等。
BOM:浏览器对象模型(Browser Object Model),操作浏览器部分功能的API。比如让浏览器自动滚动。
var div1 = document.getElementById("box1"); //方式一:通过 id 获取 一个 元素节点(为什么是一个呢?因为 id 是唯一的)
var arr1 = document.getElementsByTagName("div"); //方式二:通过 标签名 获取 元素节点数组,所以有s
var arr2 = document.getElementsByClassName("hehe"); //方式三:通过 类名 获取 元素节点数组,所以有s
offset 属性
js中有一套方便的获取元素尺寸的办法就是offset家族。offset家族包括:
offsetWidth
offsetHight
offsetLeft
offsetTop
offsetParent
offsetWidth 和 offsetHight
offsetWidth 和 offsetHight:获取元素的宽高 + padding + border,不包括margin
offsetParent:获取当前元素的定位父元素。
如果当前元素的父元素,有CSS定位(position为absolute、relative、fixed),那么 offsetParent 获取的是最近的那个父元素。
如果当前元素的父元素,没有CSS定位(position为absolute、relative、fixed),那么offsetParent 获取的是body。
offsetLeft 和 offsetTop
offsetLeft:当前元素相对于其定位父元素的水平偏移量。
offsetTop:当前元素相对于其定位父元素的垂直偏移量。
//方法1:用offsetLeft获取值,用style.left赋值。
div.style.left = div.offsetLeft + 100 + 'px';
scroll 相关
scrollHeight 的特点是:如果内容超出了盒子,scrollHeight为内容的高(包括超出的内容);如果不超出,scrollHeight为盒子本身的高度。ScrollWidth同理。
scrollLeft:获取水平滚动条滚动的距离。
scrollTop:获取垂直滚动条滚动的距离。
window.scrollTo()方法举例:返回到顶部小火箭
事件绑定
element.onclick = function () { }
element.addEventListener('click', function () { }, false);
一个元素的一个事件,可以绑定多个响应函数。不存在响应函数被覆盖的情况。执行顺序是:事件被触发时,响应函数会按照函数的绑定顺序执行。
addEventListener()中的this,是绑定事件的对象。
事件传播
事件传播的三个阶段是:事件捕获、事件冒泡和目标。
事件捕获阶段:事件从祖先元素往子元素查找(DOM树结构),直到捕获到事件目标 target。在这个过程中,默认情况下,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡阶段:事件从事件目标 target 开始,从子元素往冒泡祖先元素冒泡,直到页面的最上一级标签。
事件冒泡: 当一个元素上的事件被触发的时候(比如说鼠标点击了一个按钮),同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到DOM树的最上层。
通俗来讲,冒泡指的是:子元素的事件被触发时,父元素的同样的事件也会被触发。取消冒泡就是取消这种机制。
阻止冒泡
event.stopPropagation();
鼠标键盘事件
鼠标的拖拽事件
拖拽的流程:
(1)onmousedown:当鼠标在被拖拽元素上按下时,开始拖拽;
(2)onmousemove:当鼠标移动时被拖拽元素跟随鼠标移动;
(3)onmouseup:当鼠标松开时,被拖拽元素固定在当前位置
鼠标的滚轮事件
onmousewheel:鼠标滚轮滚动的事件,会在滚轮滚动时触发。但是火狐不支持该属性。
DOMMouseScroll:在火狐中需要使用 DOMMouseScroll 来绑定滚动事件。注意该事件需要通过addEventListener()函数来绑定。
键盘事件
事件名
onkeydown:按键被按下。
onkeyup:按键被松开。
注意:
如果一直按着某一个按键不松手,那么,onkeydown事件会一直触发。此时,松开键盘,onkeyup事件会执行一次。
当onkeydown连续触发时,第一次和第二次之间会间隔稍微长一点,后续的间隔会非常快。这种设计是为了防止误操作的发生。
BOM
常见的 BOM对象有:
Window:代表整个浏览器的窗口,同时 window 也是网页中的全局对象。
Navigator:代表当前浏览器的信息,通过该对象可以识别不同的浏览器。
Location:代表当前浏览器的地址栏信息,通过 Location 可以获取地址栏信息,或者操作浏览器跳转页面。
History:代表浏览器的历史记录,通过该对象可以操作浏览器的历史记录。由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效。
Screen:代表用户的屏幕信息,通过该对象可以获取用户的显示器的相关信息。
History 对象
History对象:可以用来操作浏览器的向前或向后翻页
History对象的属性
history.length解释:获取浏览器历史列表中的 url 数量。注意,只是统计当次的数量,如果浏览器关了,数量会重置为1。
History对象的方法
方法1:
history.back();
解释:用来回退到上一个页面,作用和浏览器的「回退按钮」一样。
方法2:
history.forward();解释:用来跳转下一个页面,作用和浏览器的「前进按钮」一样。
方法3:
history.go( int n); // 需要整数作为参数 // 代码举例: history.go( 1 ); // 向前跳转一个页面,相当于 history.forward() history.go( 2 ); // 表示向前跳转两个页面 history.go( 0 ); // 刷新当前页面 history.go( -1 ); // 向后跳转一个页面,相当于 history.back() history.go( -2 ); // 向后跳转两个页面
解释:向前/向后跳转 n 个页面。
console.log(location.href); // 获取当前页面的url 路径
Location 对象的方法
方法1:
location.assign(str);
解释:用来跳转到其他的页面,作用和直接修改location.href一样。
方法2:
location.reload();
解释:用于重新加载当前页面,作用和刷新按钮一样。
代码举例:
location.reload(); // 重新加载当前页面。
location.reload(true); // 在方法的参数中传递一个true,则会强制清空缓存刷新页面。
location.replace();
解释:使用一个新的页面替换当前页面,调用完毕也会跳转页面。但不会生成历史记录,不能使用「后退按钮」后退。
定时器
定时器的常见方法
setInterval():循环调用。将一段代码,每隔一段时间执行一次。(循环执行)
setTimeout():延时调用。将一段代码,等待一段时间之后再执行。(只执行一次)
clearInterval(参数1)就可以清除定时器。
jquery
1)链式编程:比如.show()和.html()可以连写成.show().html()。
链式编程原理:return this。
通常情况下,只有设置操作才能把链式编程延续下去。因为获取操作的时候,会返回获取到的相应的值,无法返回 this。
(2)隐式迭代:隐式 对应的是 显式。隐式迭代的意思是:在方法的内部会为匹配到的所有元素进行循环遍历,执行相应的方法;而不用我们再进行循环,简化我们的操作,方便我们调用。
如果获取的是多元素的值,大部分情况下返回的是第一个元素的值
区别一:书写个数不同:
Js 的入口函数只能出现一次,出现多次会存在事件覆盖的问题。
jQuery 的入口函数,可以出现任意多次,并不存在事件覆盖问题。
区别二:执行时机不同:
Js的入口函数是在所有的文件资源加载完成后,才执行。这些文件资源包括:页面文档、外部的js文件、外部的css文件、图片等。
jQuery的入口函数,是在文档加载完成后,就执行。文档加载完成指的是:DOM树加载完成后,就可以操作DOM了,不用等到所有的外部资源都加载完成
var myBox = document.getElementById("box"); //通过 id 获取单个元素
var boxArr = document.getElementsByClassName("box"); //通过 class 获取的是数组
var divArr = document.getElementsByTagName("div"); //通过标签获取的是数组
通过 jQuery 获取这些元素节点的方式是:(获取的都是数组)
//获取的是数组,里面包含着原生 JS 中的DOM对象。
var jqBox1 = $("#box");
var jqBox2 = $(".box");
var jqBox3 = $("div");
jquery 动画
显示动画
方式一:
$("div").show();
解释:无参数,表示让指定的元素直接显示出来。其实这个方法的底层就是通过display: block;实现的。
方式二:
$("div").show(2000);
解释:通过控制元素的宽高、透明度、display属性,逐渐显示,2秒后显示完毕。
$("div").show("slow");
参数可以是:
slow 慢:600ms
normal 正常:400ms
fast 快:200ms
解释:和方式二类似,也是通过控制元素的宽高、透明度、display属性,逐渐显示。
方式四:
//show(毫秒值,回调函数;
$("div").show(5000,function () {
alert("动画执行完毕!");
});
动画执行完后,立即执行回调函数。
1、滑入动画效果:(类似于生活中的卷帘门)
$(selector).slideDown(speed, 回调函数);
解释:下拉动画,显示元素。
注意:省略参数或者传入不合法的字符串,那么则使用默认值:400毫秒(同样适用于fadeIn/slideDown/slideUp)
2 滑出动画效果:
$(selector).slideUp(speed, 回调函数);
解释:上拉动画,隐藏元素。
3、滑入滑出切换动画效果:
$(selector).slideToggle(speed, 回调函数);
淡入淡出动画
1、淡入动画效果:
$(selector).fadeIn(speed, callback);
作用:让元素以淡淡的进入视线的方式展示出来。
2、淡出动画效果:
$(selector).fadeOut(1000);
作用:让元素以渐渐消失的方式隐藏起来
3、淡入淡出切换动画效果:
$(selector).fadeToggle('fast', callback);
jquery 懒加载
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
div {
height: 3000px;
background-color: pink;
}
</style>
<script src="jquery-1.11.1.js"></script>
<!--懒加载的使用。第一步:导包(必须在jquery库的下方)-->
<script src="jquery.lazyload.js"></script>
<script>
$(function () {
//第二步骤:调用懒加载的方法实现功能。参数的不同,功能也不同。
$("img.lazy").lazyload();
})
</script>
</head>
<body>
<div></div>
<!--需要实现将图片设置为懒加载模式-->
<img class="lazy" data-original="images/01.jpg" width="640" height="480">
</body>
</html>
原型对象
如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的实例对象中都会有一个隐含的属性,指向该构造函数的原型,我们可以通过__proto__来访问该属性。
代码举例:
// 定义构造函数
function Person() {}
var per1 = new Person();
var per2 = new Person();
console.log(Person.prototype); // 打印结果:[object object]
console.log(per1.__proto__ == Person.prototype); // 打印结果:true
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样就不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
认识3:
使用 in 检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true。
可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性。
原型链
原型对象也是对象,所以它也有原型,当我们使用或访问一个对象的属性或方法时:
它会先在对象自身中寻找,如果有则直接使用;
如果没有则会去原型对象中寻找,如果找到则直接使用;
如果没有则去原型的原型中寻找,直到找到Object对象的原型。
Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回 null
//方式一:重写 Person 原型的toString方法。针对 Person 的所有实例生效
Person.prototype.toString = function() {
return (
"Person[name=" +
this.name +
",age=" +
this.age +
",gender=" +
this.gender +
"]"
);
};
程序运行过程中会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢。所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用时,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
上面这句话,也可以这样理解:如果堆内存中的对象,没有任何变量指向它时,这个堆内存里的对象就会成为垃圾。
JS拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁。我们不需要也不能进行垃圾回收的操作。我们仅仅需要做的是:如果你不再使用该对象,那么,将改对象的引用设置为 null 即可。
原型链
所有的引用类型(数组、对象、函数),都具有对象特性,都可以自由扩展属性。null除外。
所有的引用类型(数组、对象、函数),都有一个_proto_属性,属性值是一个普通的对象。_proto_的含义是隐式原型。
所有的函数(不包括数组、对象),都有一个prototype属性,属性值是一个普通的对象。prototype的含义是显式原型。(实例没有这个属性)
所有的引用类型(数组、对象、函数),_proto_属性指向它的构造函数的prototype值。
ES6
babel 的作用是将 ES6 语法转为 ES5 语法,支持低端浏览器。
针对整个脚本文件:将use strict放在脚本文件的第一行,则整个脚本文件将以严格模式运行。
针对单个函数:将use strict放在函数体的第一行,则整个函数以严格模式运行。
语法和行为改变
必须用var声明变量
禁止自定义的函数中的this指向window
创建eval作用域
对象不能有重名的属性
全局变量显式声明
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。
严格模式下无法删除变量
属性相关
普通模式下,对一个对象的只读属性进行赋值,不会报错,只会默默地失败。严格模式下,将报错。
严格模式下,对禁止扩展的对象添加新属性,会报错。
普通模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。
普通模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,多个重名的参数属于语法错误。
ES5 中,使用 var 定义全局变量( var 是 variable 的简写)。
ES6 中,新增了 let 和 const 来定义变量:
let:定义局部变量,替代 var。
const:定义常量(定义后,不可修改)。
通过上面两个例子可以看出,用 let 声明的变量,只在局部(块级作用域内)起作用。
我们要习惯用 let 声明,减少 var 声明带来的污染全局空间。
eS6 之前的普通函数中:this 指向的是函数被调用的对象(也就是说,谁调用了函数,this 就指向谁)。
而 ES6 的箭头函数中:箭头函数本身不绑定 this,this 指向的是箭头函数定义位置的 this(也就是说,箭头函数在哪个位置定义的,this 就跟这个位置的 this 指向相同)。
function fn(param = 'hello') {
console.log(param);
}
在 ES6 中定义方法时,我们可以给方法里的参数加一个默认值(缺省值):
方法被调用时,如果没有给参数赋值,那就是用默认值;
方法被调用时,如果给参数赋值了新的值,那就用新的值。
const fn = (...args) => {
//当不确定方法的参数时,可以使用剩余参数
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);
};
fn(1, 2);
fn(1, 2, 3); //方法的定义中了四个参数,但调用函数时只使用了三个参数,ES6 中并不会报错。
let str = 'abcdefg';
console.log(str.includes('a')); //true
console.log(str.includes('h')); //false
//startsWith(str) : 判断是否以指定字符串开头
console.log(str.startsWith('a')); //true
console.log(str.startsWith('d')); //false
//endsWith(str) : 判断是否以指定字符串结尾
console.log(str.endsWith('g')); //true
console.log(str.endsWith('d')); //false
//repeat(count) : 重复指定次数a
console.log(str.repeat(5));
ES6 提供了 新的数据结构 Set。Set 类似于数组,但成员的值都是唯一的,没有重复的值。
premise
// 第一步:model层的接口封装
const promise = new Promise((resolve, reject) => {
// 这里做异步任务(比如ajax 请求接口。这里暂时用定时器代替)
setTimeout(function () {
var data = { retCode: 0, msg: 'qianguyihao' }; // 接口返回的数据
if (data.retCode == 0) {
// 接口请求成功时调用
resolve(data);
} else {
// 接口请求失败时调用
reject({ retCode: -1, msg: 'network error' });
}
}, 100);
});
// 第二步:业务层的接口调用。这里的 data 就是 从 resolve 和 reject 传过来的,也就是从接口拿到的数据
promise
.then((data) => {
// 从 resolve 获取正常结果
console.log(data);
})
.catch((data) => {
// 从 reject 获取异常结果
console.log(data);
});
sync/await 的基本用法
async 后面可以跟一个 Promise 实例对象。代码举例如下:
const request1 = function() {
const promise = new Promise(resolve => {
request('https://www.baidu.com', function(response) {
if (response.retCode == 200) {
// 这里的 response 是接口1的返回结果
resolve('request1 success'+ response);
} else {
reject('接口请求失败');
}
});
});
return promise;
};
async function queryData() {
const response = await request1();
return response;
}
queryData().then(data => { console.log(data); });
symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
其他
var 声明的变量会挂载在 window 对象上,而 let 和 const 声明的变量不会
var 声明的变量存在变量提升,let 和 const 声明的变量不存在变量提升
const:一旦声明必须赋值;声明后不能再修改
如果用 const 声明基本数据类型,则无法被修改;
如果用 const 声明引用数据类型(即“对象”),这里的“无法被修改”指的是不能改变内存地址的引用;但对象里的内容是可以被修改的。
bind()
都能改变this的指向
call()/apply()是立即调用函数
bind()是将函数返回,因此后面还需要加()才能调用