ES语法
1、面向对象和面向过程
1、程序设计分为面向对象和面向过程
2、面向过程是分析出解决问题所需要的步骤,然后用函数一步步实现,使用的时候再调用就可以了
3、面向对象就是把事物分解成一个个对象,然后由对象分工合作。每一个对象都是功能中心,具有明确分工
4、面向对象的特性:封装性、继承性、多态性
5、简单理解:面向过程就是面对步骤,一步一步的操作,这样联系紧密,但出现错误不容易维护,面向对象就是让对象去处理东西,哪一个对象出现问题,就找对象处理,这样容易维护耦合性低
2、类和对象
1、类是泛指,指描述的某一类的事物,而对象则是具体的事物
2、面向对象编程,要考虑需要哪些对象,创建对象来做哪些事情
3、面向对象:创建共有的属性和方法成为一个类,通过类去实例化出对象,对象去做事情
4、在生活中,对象是具体的某一个事物,但在编程中,对象是一组没有顺序的属性和方法的几何,例如字符串、 数组、函数等
5、对象是由属性和方法组成:
属性:对象的特征
方法:对象的行为
6、在ES6中增加了类的概念。使用class关键字声明。用类来实例化对象
7、对象特指某一个,通过类实例化一个具体的对象。
8、创建类:
/* 注意:类必须使用new来实例化对象*/
class People{
}
/*创建实例*/
var xh=new People()
9、constructor:这是一个方法,是类的构造函数,用于传递参数,返回传递参数,返回实例对象,通过new 命令生成对象实例时,自动调用该方法,如果没有显示定义,类内部会自动给我们创建一个constructor。
10、继承:子类可以继承父类的一些属性和方法
class P1 {
}
class P2 extends P1{
}
3、类的继承和super
1、注意:子类可以继承父类的属性和方法,但如果子类定义了和父类相同的属性名,那么使用的就是子类自己的属性,如果子类的实例使用父类的方法中有关于父类自己的属性,将会报错,如图1. 因为没有调用父类的方法,父类方法里面的参数的this指向父类,没有参数传递
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KAS7HDNG-1682322406497)(C:\Users\杨怡然\Desktop\常用前端框架及工具\拓展学习资料\1.PNG)]
2、上面的这个问题可以通过super关键字来解决,super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。super接收到的参数会传给父类
3、super必须放到子类this之前,super方法必须在子类的构造函数里面使用
class Father {
constructor(x) {
this.x = x
}
tt() {
console.log(this.x);
}
}
class son extends Father {
constructor(x) {
/*调用父类的构造函数*/
super(x)
}
}
var s=new son(5)
console.log(s.x);
s.tt()
4、函数重载
1、就是子类继承了父类,但是自己有的方法就用自己的,相当于覆写,自己有的先用自己的,没有就去用父类的
2、可以通过super.父类方法名来调用父类的方法
5、注意点
1、类是没有变量提升的,必须先定义类,才能实例化对象,使用类里面的属性和方法
2、类里面的共有的属性和方法一定要加this使用。因为this指向当前对象
3、类里面的this指向
4、constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者
6、构造函数和原型
目的:1、能够使用构造函数创建对象
2、能够说出原型的作用
3、能够说出访问对象成员的规则
4、能够使用Es新增的一些方法
6-1 构造函数
1、创建对象的三种方式:
1:对象字面量
2:new Object()
3:自定义构造函数
2、构造函数是一种特殊的函数,主要用来初始化对象,为对象成员赋初始值,与new一起使用,可以把一些公共 的属性和方法抽取出来,然后封装到这个函数里面。
3、 静态成员和实例成员:
实例成员:构造函数内部通过this添加的成员,只能通过实例化的对象来访问,不可以通过构造函数访问
静态成员:在构造函数本身上添加的成员:构造函数.属性名=值 ,该属性就是静态成员,静态属性只能 通过构造函数访问,不能通过对象访问
4、构造函数好用,但是存在浪费内存的问题
因为创建出来的对象都有自己单独的内存空间。
5、
<script>
// 1、 利用new Object创建
var obj = new Object()
console.log(obj);
// 2、利用对象字面量
var obj2 = {}
console.log(obj2);
// 3、利用构造函数
function Obje(name) {
this.name = name
this.ff = function () {
console.log(12);
}
}
var obj3 = new Obje('小红')
console.log(obj3);
</script>
6-2 原型-prototype
1、构造函数通过原型分配的函数是所有对象共享的
2、每一个构造函数都有一个prototype属性,指向另一个对象,这个prototype就是一个对象,这个对象的 所有属性和方法,都会被构造函数所拥有
3、prototype也叫作原型对象,可以把不变的方法定义到prototype上面,这样所有的对象的实例就可以共 享这些方法,
4、// 因为创建的对象开辟的不同的内存地址
// 所以在构造函数里面创建的方法也存在了不同的地方
// 所以看两个对象的构造函数创造的方法是否相等时
// 显示的是false。
// 而通过原型对象创建的方法是共享方法,所有对象都可以用
// 指向了同一个位置,所以测试是否相等时显示是true
// 所以很节约内存空间
6-3 对象原型 __ proto__
1、对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以对象可以使用构造函数 prototype原型对象的属性和方法,就是因为有__proto__原型的存在
2、__proto__是对象使用,指向原型对象的
prototype是构造函数指向原型对象的
constructor是原型对象指向构造函数的本身
3、如果把原型对象写成对象的形式,那么将会覆盖掉constructor这个属性,就无法再指向构造函数
这个对象形式把原来的constructor属性覆盖了。不能在去寻找到原来的构造函数,除非手动添加一个 constructor属性指向构造函数
function People(name, age) {
this.name = name
this.age = age
this.ff = function () {
console.log("这是people的方法");
}
}
// 这个对象形式把原来的constructor属性覆盖了
// 不能在去寻找到原来的构造函数,除非手动添加
// 一个constructor属性指向构造函数
People.prototype = {
eat: function () {
console.log("吃饭");
},
drink: function () {
console.log("喝水");
}
}
var p=new People()
console.log(People.prototype.constructor);
console.log(p.__proto__.constructor);
6-4 构造函数、实例、原型对象之间的关系:原型链
1、构造函数通过prototype指向原型对象,原型对象通过constructor指向构造函数
实例对象通过 __proto__指向原型对象,每一个对象都有这样一个__proto__指向原型对象
2、构造函数也有__proto__,指向的是Object的原型对象,而Object原型对象的__proto__找不到原型对象就会指向null,这样链起来的关系就叫原型链。
3、原型对象上绑定的对象可以通过构造函数的实例对象得到,我们可以通过原型链找到上一个原型对象
所以,在上一个原型对象上绑定的属性和方法,我们依然可以通过实例对象获得
function People(name, age) {
this.name = name
this.age = age
this.ff = function () {
console.log("这是people的方法");
}
}
Object.prototype.w=54
var p=new People()
console.log(p.w);
//实例对象可以跟着原型链查找这个属性或者方法,查找到就可以使用
4、如果对象的属性或方法和原型链上的原型对象重复,优先使用自己定义的属性和方法
6-5 通过原型对象可以给内置对象添加扩展方法
1、通过原型对象给内置对象扩展方法
// 通过原型对象可以扩展内置对象方法
// 谁调用这个方法,this就指向谁
Array.prototype.sum = function () {
let s = 0
for (let i = 0; i < this.length; i++) {
s += arr[i]
}
return s
}
var arr=[1,2,3,4,5]
console.log(arr.sum());
7、继承
1、在ES6之前并没有提供extends继承,可以通过构造函数+原型对象模拟实现继承,被称为组合继承
2、call()调用这个函数,并且修改函数运行时的this指向
函数.call(this指向,其他参数)
function fn() {
console.log(this);
}
// 调用1
fn()
// 调用2
fn.call()
// 3、对象调用
var o={}
fn.call(o)
3、借用call方法继承属性:在子对象里面调用父对象的call方法,并且把参数传入到父对象中去。这个只能 继承属性
4、在父函数里面使用原型对象定义的方法是不能被子函数使用的,使用call函数继承的只是父函数的属性。只 是调用构造函数,没有调用原型对象,所以是不能继承方法的。如果让子函数的原型对象=父函数的原型对象,会乱套
function fa(name) {
this.name = name
}
fa.prototype.way = function () {
console.log("父亲的方法");
}
function son(name, age) {
this.age = age
// 只是调用构造函数,没有调用原型对象,所以是不能继承方法的
fa.call(this, name)
}
var s = new son("小红", 18)
console.log(s);
son.prototype=fa.prototype
son.prototype.ff=function(){
console.log("儿子的方法");
}
console.log(fa.prototype);
打印父函数的原型对象会发现我们是让子函数的原型对象地址指向了父函数的原型对象,这样,子函数有改变会复函数会跟着改变
5、 function fa(name) {
this.name = name
}
fa.prototype.way = function () {
console.log("父亲的方法");
}
function son(name, age) {
this.age = age
// 只是调用构造函数,没有调用原型对象,所以是不能继承方法的
fa.call(this, name)
}
son.prototype = new fa()
son.constructor = son
// 相当于父函数创建了一个实例对象,实例对象赋值给了子函数的原型对象
var s = new son("小红", 18)
s.way()
7-1 类
1、类本质上是一个函数。可以简单的认为,类就是构造函数的另一种写法
2、类也有原型对象prototye
3、类也可以通过原型对象添加方法
4、通过类创建的实例也有__proto__
5、ES6的类就是语法糖:指一种便捷写法,有两种方法实现功能,但一种更方便、更快,这种方法就是语法糖
8、ES5新增方法
8-1 数组方法
1、forEach():遍历数组元素
注意:只是遍历,没有返回值
使用:array.forEach(),foreach里面是一个回调函数,里面是三个参数:数组元素、索引号、数组
forEach迭代数组
var arr = [1, 5, 6]
arr.forEach(function (value, index, array) {
console.log("数组元素" + value);
console.log("索引号" + index);
console.log("数组" + array);
})
2、filter():创建一个新的数组,新数组中的元素通过检查指定数组中符合条件的所有元素,主要用于筛 选数组。
注意:直接返回一个新数组。不会影响到原数组的值
使用:array.filter()里面是一个回调函数,里面是三个参数:数组元素、索引号、数组
// filter筛选数组,筛选大于50的数,返回的是一个新数组
var arr = [10, 50, 23, 66, 44, 51]
var arr1 = arr.filter(function (value) {
return value > 50
})
console.log(arr1);
3、some():检测数组中的元素是否满足指定条件,就是查找数组中否有满足条件的元素 ,有的话就返回 true,没有就返回false,
注意:如果找到第一个满足条件的元素,就终止循环,不在继续寻找
使用:array.some()里面是一个回调函数,里面是三个参数:数组元素、索引号、数组
// some:筛选是否有满足条件的元素,返回布尔值
var arr = [2, 8, 5, 44, 11, 33, 78, 57, 14]
var t = arr.some(function (v) {
return v % 5 == 0
})
console.log(t);
4、foreach和some的不同:foreach是在迭代,也就是遍历数组元素,return不会停止遍历,二some满足 条件就会终止遍历,some的效率高于foreach,filter遇到return也不会停 止迭代
5、from:将类数组或可遍历对象转换为真正的数组,必须有length属性,还可以接受第二个参数,作用类似 于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
var obj = {
"0": "小红",
"1": 18,
"length":2
}
var o=Array.from(obj)
console.log(o);
8-2 字符串方法
1、trim():从一个字符串的两端删除空白字符
注意:不影响字符串本身,返回的是一个新的字符串
使用:var b=str.trim()
// trim:去除字符串两侧空格
var str = ' mary '
console.log(str);
console.log(str.trim());
2、includes:表示是否找到了参数字符串。字符串使用这个方法。返回值是布尔值
var str = "Hello World"
var t = str.includes('o')
console.log(t);
3、startsWith:表示参数字符串是否在原字符串的头部,字符串使用这个方法。返回值是布尔值
var str = "kjashdfk"
console.log(str.startsWith("l"));
console.log(str.startsWith("k"));
4、endWith::表示参数字符串是否在原字符串的尾部,字符串使用这个方法。返回值是布尔值
var str = "kjashdfk"
console.log(str.endsWith("l"));
console.log(str.endsWith("k"));
5、repeat:返回一个新的字符串,表示将原字符串重复n次
1、 如果参数是小数,会被取整,而且是向下取整
2、如果参数是0,字符串为空字符串
3、如果参数是负数,会报错
var s = "o"
console.log(s.repeat(5));
console.log(s);
//1、 如果参数是小数,会被取整,而且是向下取整
console.log(s.repeat(2.99));
// 2、如果参数是0,字符串为空字符串
console.log(s.repeat(0));
// 3、如果参数是负数,会报错
console.log(s.repeat(-2));
6、padStart:返回一个新的字符串 补全字符串长度,方法里面有两个参数,一个是设置字符串的长度,一 个是补全原字符串的字符,记住,是在原字符串的开头补齐
var str = "kl"
console.log(str.padStart(5,"a"));
7、padEnd:返回一个新的字符串 补全字符串长度,方法里面有两个参数,一个是设置字符串的长度,一 个是补全原字符串的字符,记住,是在原字符串的结尾补齐
var str = "kl"
console.log(str.padEnd(5,'a'));
8-3对象方法
1、keys():用于获取对象自身的所有属性
注意;返回一个由属性名组成的数组
使用:Object.keys(obj):
// keys方法用于获取对象自身的所有属性,相当于for。。in
var obj = {
uname: '小红',
age: 18
}
var arr = Object.keys(obj)
console.log(arr);
2、defineProperty:定义新属性或修改原有的属性
注意:这个方法有三个参数,不能省略
obj:目标对象
prop:需要定义的属性的名字
desc:目标属性所拥有的特性,以对象的形式书写
{
value:设置属性的值,默认为undefined,有的话就是修改,没有就是添加
writable:值是否可以重写。true,false,默认false
enum:目标属性是否可以被枚举,true/false,默认是false
config:目标属性是否可以被删除,或者再次被修改,true/false。默认是false,使 用delete 删除属性也删除不了
}
使用:
// defineProperty:修改或定义属性
var obj = {
name: '小红',
age: 18
}
// 1、增加属性
obj.height = 180
console.log(obj);
// 2、使用方法
Object.defineProperty(obj,'num',{
value:120
})
console.log(obj);
3、Object.values:返回一个数组,成员是参数对象自身可遍历属性的键值。
var obj = {
name: '小红',
age: 18
}
console.log(Object.values(obj));
Object.defineProperty(obj,'h',{
value:170,
enumerable:false
})
console.log(Object.values(obj));
Object.defineProperty(obj,'w',{
value:170,
enumerable:true
})
console.log(Object.values(obj));
4、Object.entries:返回一个数组,成员是参数对象自身的所有可遍历属性的键值对数组
var obj = {
name: '小红',
age: 18
}
console.log(Object.entries(obj));
// 因为不可遍历
Object.defineProperty(obj, 'h', {
value: 170,
enumerable: false
})
console.log(Object.entries(obj));
//可遍历,所以数组有
Object.defineProperty(obj, 'w', {
value: 170,
enumerable: true
})
console.log(Object.entries(obj));
5、Object.is:判断两个对象是否相等。判断的是全等。
8-4、数值方法
1、isFinte:如果number是有限数,那么返回true
var n = 55
console.log(Number.isFinite(n));
2、isNaN:如果number是非数字,或者是正、负无穷大的数,返回false
3、 Number.parseInt():取整,只保留整数部位
4、Number.parseFloat():只保留数字部分,如果是字符串类型的数字,只保留数字在前面的,数字在后面 的转成NaN值。
var s=0.9999999
var t="123df"
var o="df123"
console.log(Number.parseFloat(s));
console.log(Number.parseFloat(t));
console.log(typeof Number.parseFloat(t));
console.log(Number.parseFloat(o));
8-5、Math方法
1、Math.trunc():去掉一个数的小数部分。返回整数部分,对于非数值,先使用Number方法转化为数值。对 于无法取整的数返回NaN值,对于空值返回0
// 1、去掉一个数的小数部分。返回整数部分
console.log(Math.trunc(0.99));
console.log(Math.trunc(1.01));
// 2、对于非数值,先使用Number方法转化为数值
console.log(Math.trunc("123"));
// 3、对于无法取整的数返回NaN值
console.log(Math.trunc("str"));
console.log(Math.trunc(NaN));
console.log(Math.trunc(undefined));
// 4、对于空值返回0
console.log(Math.trunc(null));
console.log(Math.trunc(false));
2、Math.sign():用来判断一个数到底是正数、负数、还是0,对于非数值,会先将其转换为数值
返回五种值:
参数为正数:返回1
参数为负数,返回-1
参数为0,返回0
参数为-0,返回-0
其他值,返回NaN
3、Math.imul():返回两个数相乘的结果
9、函数
学习目标
1、说出函数的多种定义和调用方式
2、能够说出和改变函数内部的this指向
3、能够说出严格模式的特点
4、能够吧函数作为参数和返回值传递
5、能够说出闭包的作用
6、递归的两个条件
7、说出浅拷贝和深拷贝的区别
9-1 函数的定义和调用
1、函数声明的三种方式:
1、命名函数
2、匿名函数
3、构造函数:new Function('参数1',‘参数2’,‘函数体)
// 1、命名函数
function fn() {
}
// 2、匿名函数
var fnn = function () {
}
// 3、了解 new Function('参数1',‘参数2’,‘函数体)
var f = new Function('console.log(123)')
f()
2、函数也是对象,也有__proto__,所有函数都是Function的实例对象
3、函数的调用
1、普通函数 fn() fn.call()
2、对象的方法 通过对象调用 o.fnn()
3、构造函数 通过new关键字调用 new Peo()
4、绑定事件函数 通过绑定的事件类型进行调用 btn.onclick
5、定时器函数,.定时器自己调用
6、立即执行函数.不需要调用,立即执行
(function(){(function(){
})()
4、rest参数:rest参数:代替arguments,用于获取函数的多余参数。 可以向函数传入任意数目的参数,
// 1、只有一个参数
function fn(...val) {
let s = 0
for (var k of val) {
s += k
}
return s
}
console.log(fn(1, 2, 3, 4, 5));
// 2、两种参数
function fun(x, ...val) {
let s = x
for (var k of val) {
s += k
}
return s
}
console.log(fun(5, 1, 2, 3, 4, 5));
9-2 this说明
1、 在js中提供了this。this的中文意思是这,用来指向一个对象。比如说这函数对象,这个window对象,这个obj等等。在js中,因为一些原因,我们需要指向一个对象,例如,使用全局变量给几个按钮绑定单击事件,但按钮的单击事件函数是一个回调函数,当按钮单击后事件才发生,此时的变量已经变化完毕,按钮[i]既是undefined值,是会报错的,因此,我们就需要用this来指向那个单击事件发生时绑定单击事件的按钮对象,下面是代码
在这段代码中,我获取了所有的button按钮,然后分别给他们安装了单击事件,因为全局变量i在按钮绑定的回调事件发生之前已经走到了最大值,所以无法给按钮成功绑定上单击事件,所以需要用到this,指向的就是那个绑定单击事件的那个对象
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
btns[i].onclick=function(){
for (var j = 0; j < btns.length; j++) {
btns[j].style.color = ""
btns[j].style.backgroundColor = ""
}
this.style.color = "red"
this.style.backgroundColor = "green"
}
}
this的具体指向
1、在全局作用域中,this指向window对象,window对象是顶级对象,document也是它的孩子。
2、在普通方法中,this也指向window对象
3、对象调用方法时,this指向的就是那个调用方法的对象,在下面我会进行举例
1、全局作用域
console.log(this);
2、普通函数使用
function fun() {
console.log(this);
}
fun()
以上输出的都是window对象
3、对象调用方法,this就指向这个对象
var btn = document.querySelector('button')
btn.onclick = function () {
console.log(this);
}
这里的this就是btn这个按钮对象,因为是按钮对象在调用这个单击方法。
4、对象调用方法
var a=5
function fun() {
console.log(this.a);
}
// 对象调用方法,this就指向这个对象
var obj = {
a:2,
fun: function(){
fun()
}
}
// 对象.属性值: obj.属性
// 对象方法的使用:obj.fun()
obj.fun()
在这里使用obj去调用fun方法,这个this同样的指向的是obj
this的规则
为了大家更好的认识到this。会提供一些规则来认识到在运行环境中,this到底指向谁,也会认识到我们可以通过这个this对那个指向对象进行属性的读写操作。
1、圆括号直接调用函数,该对象指向window
2、对象打点调用函数,上下是对象是对象 对象.方法名
3、从数组中枚举出函数圆括号运行,this是数组 数组[index]()
4、立即执行函数、延时器、定时器调用的函数this指向window
5、事件监听,this是绑定监听的这个元素。
6、补充:不端在规则里面,但应该了解
函数的apply方法和call方法指向
函数.call()、函数.call():是在执行函数
但如果在括号里面传入对象,那么就可以将对象作为执行传给函数, this就指向该对象
再补充(call和apply的区别)
ps:当函数有参数需要进行传递的时候,call方法可以直接写入参数,但apply方法必须是把参数写成数组的形式
函数的不同调用方式决定了this的指向不同
用functoion定义的函数和箭头函数接人不同,function定义的函数看如何调用,箭头函数看如何定义,首先讲解function定义的函数。
function定义的函数,如果是圆括号直接调用,上下文是window
function定义的函数,如果是对象打点直接调用,上下文是对象
function定义的函数,如果是数组枚举加圆括号,上下文是数组
function定义的函数,如果是定时器、延时器,上下文是window
function定义的函数,如果是事件处理函数,上下文是绑定事件的对象
function定义的函数,支持用call和apply来更改上下文
然而,箭头函数截然不然,看定义时候的大环境,会寻找包裹箭头函数的函数环境,它的this由这个函数的上下文对象所决定,如果这个函数没有对象传入,那么这个函数的上下文是window,箭头函数的上下文也是,如果是对象传入该函数,那么箭头函数的this也是该对象,不接受call和apply,终生不会改变
//规则1
// 函数被定义在obj里面
// 通过变量获得直接用变量调用,指向的是window
var obj = {
a: 6,
fun: function () {
console.log(this.a);
}
}
var a=3
var f=obj.fun
f()
//规则2
// 对象打点调用函数,this指向这个对象
var obj = {
a: 6,
fun: function () {
console.log(this.a);
}
}
var a=3
obj.fun()
//规则3
var arr=[3,4,5,8,9,fn]
function fn(){
console.log(this[0]);
}
arr[5]()
//规则4
setInterval(function(){
console.log(this);
},2000) var obj = {
a: 3,
fun: function () {
setTimeout(function () {
console.log(this.a);
}, 2000)
}
}
var a=5
obj.fun()
//结果是5,因为延时器方法指向的this是window
//规则5
var btn = document.querySelector('button')
// 回头再调用的函数
btn.onclick = function () {
console.log(this);
}
//补充
function fun() {
alert(this);
// console.log(fun);
}
// 调用函数对象的call方法
// fun.call()
// fun.apply()
// 都是让函数执行
// 改变this指向,传入的是哪个对象,这个对象就会成为函数执行时的this
// var obj = {}
// 正则对象,对象,日期对象、数组对象都可以
fun.apply()
//call和apply的区别
var a=5
var obj={
a:3
}
function fun(m,n){
console.log(this.a);
}
// 函数.apply(对象)
// 函数.call(对象)
fun.call(obj,5,6)
fun.apply(obj,[5,6])
9-3 函数方法:改变函数内部指向问题
1、Js提供了一些函数法官法帮助我们更好的处理函数内部this的指向问题,比如bind()、call()、 apply()
2、call():可以调用函数,可以改变this的指向,通过传递参数的方式
call的主要作用可以实现继承
function fa(name) {
this.name = name
}
fa.prototype.way = function () {
console.log("父亲的方法");
}
function son(name, age) {
this.age = age
// 只是调用构造函数,没有调用原型对象,所以是不能继承方法的
fa.call(this, name)
}
3、apply():可以调用函数,可以改变this的指向,通过传递参数的方式
参数要写成数组的形式
// 使用call让this指向obj
var obj = {
}
fn.call(obj,2,5,6)
// 2、apply方法,参数必须是数组形式
fn.apply(obj,[1])
4、利用apply求取数组的最大值:max是个方法调用apply方法传递对象和值
var arr = [44, 55, 11, 33, 66]
console.log(Math.max.apply(Math,arr));
5、bind():不会调用函数,但是能改变函数内部this指向,返回由指定的this值和初始化参数改造的原函数 拷贝
使用: // 3、bind改变this指向,但不会调用,返回一个新函数指向改变后的对象
function fg(){
console.log(this);
}
var fff = fg.bind(obj)
fff()
call、apply、bind总结
相同点:
都可以改变函数内部的指向
区别:
1、call和apply会调用函数,并且改变函数内部this指向
2、call和apply传递的参数不一样,call传递参数用参数1、参数2形式,apply传递参数必须是数组形 式
3、bind不会调用函数,可以改变函数内部指向
主要应用场景:
1、call经常做继承
2、apply和数组有关系,可以实现数组最大值最小值
3、改变this指向,但不调用,可以应用于定时器
10、严格模式
1、es5的严格模式就是在严格的条件下运行JS代码,消除了js语法的一些不合理、不严谨的情况
消除代码的一些不安全的地方,保证代码运行的安全,提高了编译器效率,增加运行速度。禁用了es的一些未 来可能定义的一些语法 ,比如一些保留字:extends、export、class等
2、严格模式分为脚本开启严格模式和为函数开启严格模式
3、为脚本开启严格模式:
需要在所有语句之前放一个特定词句:"use strict"
4、ES2016做了一点修改,规定只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显 示设定为严格模式,否则会报错
5、严格模式下的变化:
1、变量必须先声明在使用
2、不能随意删除已经声明好的变量
3、严格模式下的全局作用域中的普通函数的this指向undefined
4、严格模式下,如果构造函数不加new调用,this会报错。
5、new实例化的构造函数指向创建的对象实例。
函数的变化
1、不能有重名的参数
2、函数必须声明在顶层,不允许在非函数的代码块声明函数
11、高阶函数
1、高阶函数是对其他函数进行操作的函数,它接受函数作为参数或将函数作为返回值输出。
function fn(f) {
console.log(f);
}
function ff() {
console.log(12);
}
fn(ff)
12、闭包
1、变量作用域:分为全局变量和局部变量
函数内部可以使用全局变量
函数外部不可以使用局部变量
当函数执行完毕后,本作用域内的局部变量会销毁
2、闭包:指有权访问另一个函数作用域中变量的函数
简单讲就是一个作用域可以访问另外一个函数内部的局部变量
函数外部的作用域可以访问函数内部的局部变量
闭包的主要作用就是延伸了变量的作用范围
3、函数返回什么值就是什么
4、函数定义时候的作用域,即使函数不在这个作用域执行,函数依然能够访问当初的作用域
13、递归
1、如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
2、递归效果和循环一样
3、递归也容易出现”栈溢出”的错误,也就是死循环,所以必须加退出条件return
14、浅拷贝和深拷贝
1、浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
2、深拷贝拷贝多层,每一级别的数据都会拷贝
3、浅拷贝拷贝的只是地址,指向的还是原来那个数据
4、 Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
5、 Object.assign(target, ...sources)
参数:target--->目标对象
source--->源对象
返回值:target,即目标对象
6、Object.assign() 方法是用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。当源对象的属性值发生变化时,目标对象不会受到影响,因为该方法只是复制属性值,不会建立对象之间的引用关系。如果您的源对象更改了属性值,则只会更改源对象的属性值,而不会对目标对象造成任何影响。当复制的属性里面有复合属性时,就建立了一种引用关系,复制的就是引用地址,所以会改变
7、深拷贝:开辟新的空间,拷贝的值放在新的空间
15、ES6
1、ES就是ECMA国际标准化阻止指定的一项脚本语言的标准化规范
2、ES5是2009年的标准
3、ES6也叫作ES2015
15-1 、let和const
1、var是定义全局作用域,var 只会被函数阻挡,不会被if之类的花括号阻挡
2、花括号之类都是块:比如函数、if语句、while语句等
3、let是定义块级作用域变量,会被花括号挡住
4、let和循环搭配使用
5、let没有变量提升特性,所以有暂时性死区,即必须先定义后使用
6、不能重复定义,可以重新赋值
7、const定义常量,常量的值不能改变
8、const和let定义的变量不属于window,var定义的变量属于window
9、let在块里面生效
15-2、new关键字解析
调用new
1、函数会秘密创建一个空对象{}
2、this会指向空对象
3、执行函数
4、return这个上下文对象
15-3、解构赋值(12-高阶函数)
0:...:新增的运算符,表示解构,可以挨个拆散数组或者变量的值
1、ES6允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构
2、数组中如果解构不成功,第一种值多于参数,剩余的值忽略。 第二种参数多于值,变量的值为 undefined
中括号表示解构,里面的是变量,和后面的值是一一对应的关系 ,这是自动解构
let [a, b, c] = [5, 6, 7]
console.log(a);
console.log(b);
console.log(c);
// 2、如果解构不成功,第一种值多于参数,剩余的值忽略
// 第二种参数多于值,变量的值为undefined
let [o,p]=[]
let [of,og]=[1,2,3]
console.log(o);
console.log(p);
console.log(of);
console.log(og);
// 3、可以拆散数组到另一个数组
var aaa=[1,2,3,4]
var bbb=[...aaa,5,6,7]
console.log(bbb);
3、对象解构允许使用变量的名字匹配对象属性的值,将对象属性的值赋值给变量
let p = {
name: '小红',
age: 18
}
let { name, age } = p
console.log(name);
console.log(age);
let {name:myName}=p
console.log(myName);
4、多个参数解构:
可以拆散数组到另一个数组
var aaa=[1,2,3,4]
var bbb=[...aaa,5,6,7]
console.log(bbb);
5、kv一致省略v:
var m=9
var n=4
var obj={
m:m,
n:n
}
// var obj={
// m,
// n
// }
console.log(obj);
15-4、定义箭头函数
1、定义箭头函数的方式:
括号里面是参数,花括号是函数体
()=>{}
2、箭头函数是用来简化函数定义语法的
const fn = () => {
console.log(123);
}
fn()
3、函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
const fn = () => console.log(123);
fn()
4、如果箭头函数只有一个参数,可以省略圆括号
const fn = a => console.log(a)
fn(5)
5、箭头函数中的this,是箭头函数的外部环境
如果在箭头函数中使用this,this将指向箭头函数定义位置中的this
16、Array的扩展方法
1、扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
2、扩展运算符可以合并数组
// 1、扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
let arr = [5, 8, 9, 12]
// 没有逗号,因为逗号被当做分隔符了。
console.log(...arr);
// 2、扩展运算符可以合并数组
let a = [1, 2]
let b = [4, 5]
// 两个数组已经以逗号为分隔符拆分为参数序列
let c = [...a, ...b]
let d=a.concat(b)
console.log(c);
console.log(d);
3、伪数组转换为真正的数组,就可以调用数组的方法
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var divs = document.querySelectorAll("div")
let mydiv = [...divs]
console.log(divs);
console.log(mydiv);
</script>
17、认识Promise
1、Promise是一门新的技术(ES6规范),是JS中进行异步编程的新解决方案,传统的异步:回调函数更 强大
2、它是一个构造函数,创建出来的对象用来封装一个异步操作,并且可以获取成功或者失败的值
3、异步编程:
1、fs文件操作
2、数据库操作
3、ajax异步
4、定时器
4、认识异步:异步就是不阻塞cpu,程序同时进行。
5、最典型的异步就是定时器,定时器是回调函数:满足条件后调用的函数
setTimeout(() => {
console.log(22);
}, 2000);
6、一定记住:promise支持链式调用,可以解决地狱回调的问题
7、回调黑洞:
2s输出a
4s输出b
6s输出c
1s输出d
setTimeout(() => {
console.log(a);
setTimeout(() => {
console.log(b);
setTimeout(() => {
console.log(c);
setTimeout(() => {
console.log(d);
}, 1000)
}, 6000)
}, 4000)
}, 2000)
8、回调地狱:回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调执行的条件。上面的回调地狱看着很丑,因为不堵塞cpu,都是异步任务,为了能够顺序执行,只能这样挨个写,很丑。缺点就是不便阅读,不便于异常处理。
9、如果有这么一个函数可以自己设置成同步,然后堵塞cpu,等几秒后进行执行程序。就会方便很多
deng(2000)
console.log('a')
deng(4000)
console.log('b')
10、可以自已封装这么一个deng函数,让cpu堵塞在当前程序,等待程序执行
function deng(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, ms)
})
}
async function fun() {
await deng(2000)
console.log("a");
await deng(4000)
console.log("b");
await deng(6000)
console.log("c");
}
fun()
11、所以promise启动异步任务就是给promise对象绑定回调函数
12、所谓的promise就是一个容器,里面保存着某个未来才会结束的事件。
13、promise对象有两个特点:
1、对象的状态不受外界影响,他有三种状态,只有异步操作结果,可以决定当前是什么状态
1、进行中pending
2、已完成fulfilled
3、已失败
2、一旦状态改变,就不会在变
14、promise默认是pending状态,根据异步操作的结果,要么变成已完成,要么是已失败
15、 await表示等待。await后面只能跟着promise实例 ,await只能观察promise实例的状态,他会阻 塞pending状态的promise实例.会放行成功或者失败后.
16、async:表示异步,有await语句的函数,必须加async标记。
17、resolve的返回值:
resolve圆括号中的值,会被等号左边接收。
状态变成已完成后,resolve可以返回值,但默认只能传递一个参数,但可以写成数组的形式
function wait(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve([8000,9000])
}, ms)
})
}
async function fun() {
o = await wait(2000)
console.log(o);
}
fun()
18、实例的then方法:promise实例可以打点调用then方法。 then方法可以监听promise的状态改 变。 状态改变,就会触发then方法
function deng(ms){
return new Promise(resolve=>{
setTimeout(()=>{
resolve()
},ms)
})
}
// p是promise实例一直是pending状态,3s后变为完成状态
// then方法可以传入一个函数,3s后运行这个then方法的方法
var p=deng(3000).then(()=>{
console.log(123);
})
19、then方法可以链式调用,只要在上一个then方法,继续返回一个promise实例,也就是封装的那个方 法,就可以继续调用 .then方法
function deng(ms){
return new Promise(resolve=>{
setTimeout(()=>{
resolve()
},ms)
})
}
// p是promise实例一直是pending状态,3s后变为完成状态
// then方法可以传入一个函数,3s后运行这个then方法的方法
var p=deng(3000).then(()=>{
console.log(123);
return deng(2000)
})
.then(()=>{
console.log(456);
})
20、promise补充:
1、promise实例有成功和失败的状态
2、有promise.all()、promise.race()、promise.allSettled()等方法
3、方便调试错误
25、、promise是js内置的构造函数
new一下,就可以创建
一个promise实例
他有三种状态
1、进行中pending
2、已完成fulfilled
3、已失败rejected
一旦new Promise就能够得到一个promise实例。这个实例要设置自己什么时候可以不 堵塞cpu
26、promise最大的优点就是堵塞cpu,使回调函数能够获取到变量的值,从服务器拿到数据给变量。因为从服务器拿数据不是一上来就能拿到。
27、es6.ruanyifeng.com
28、总结:axios是向服务器拿数据的异步操作,里面封装了一个promise实例,实例默认是pending状态,等异步结果成功或失败后,拿到数据或返回失败的原因。async定义函数,表示这是一个异步操作的函数,await等待这个axios异步操作的结果。
17-1、promise面试题
1、给你一个数组
var arr=[3,2,4,5,6]
根据数组元素,3s后输出☆。再2s后输出☆,以此类推
解法1:
function deng(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, ms)
})
}
var arr=[3,2,4,5,6]
async function fun(){
for(let i=0;i<arr.length;i++){
await deng(arr[i]*1000)
console.log("☆".repeat(arr[i]));
}
}
fun()
解法2:
var arr = [3, 2, 4, 5, 6]
function fn(idx) {
deng(arr[idx] * 1000).then(() => {
console.log(idx);
console.log("☆".repeat(arr[idx]));
if (idx < arr.length - 1) {
fn(idx + 1)
}
})
}
fn(0)
1、new调用关键字
用new调用函数,首先会创建一个空对象,然后this就会去指向这个空对象,然后去执行该函数,在这个函数里面用var定义的变量是不会放到创建出来的对象中去的,只有用this.属性名=值才能让该属性被放到对象中去,而且用new去调用的函数都叫做构造函数,是用来创造对象的
2、数组方法
2-1、map
1、map表示映射,表示把数组中的元素一一映射为新的一个数组
var arr = [1, 5, 6, 8, 10]
var arr2 = arr.map(item => item * 2 )
console.log(arr2);
2、原数组有多长,映射出来的新数组一定有多长,函数的返回值就是这个映射结果,不会改变原数组的结果
3、不能使用{}括起来,
4、表示返回的是一个对象的时候,要在{}外面加上()
返回对象 的名字和总价格
var arr = [
{ name: '口红', price: 180, count: 2 },
{ name: '围巾', price: 280, count: 1 },
{ name: '风衣', price: 380, count: 3 },
]
var arr2 = arr.map(item => ({
name: item.name,
sum: item.price * item.count
}))
console.log(arr2);
2-2、filter
1、filter表示过滤,从原数组中过滤出满足条件的数
// 过滤>50的数
var arr = [20, 30, 50, 70, 14, 22, 55, 66, 44, 21, 33]
console.log(arr.filter(item=>item>50));
2、filter过滤回文字符串
var arr=['moom','lop','pop','fff','eye','opl']
console.log(arr.filter(item=>item==item.split('').reverse().join('')));
2-3、reduce
1、reduce表示计算,计算数组的值,有两个参数,第一个表示结果,总数。第二个表示数组中的元素
2、能够求到数组总和
// 1、求数组元素的总和
var arr = [1,2,3,4,5]
console.log(arr.reduce((a,b)=>a+b));
3、求数组最大值
var arr = [1, 2, 3, 4, 5]
console.log(arr.reduce((a, b) => a > b ? a : b));
4、返回长度最长的数组元素
var arr=["我爱你","天气真好","好好学习天天向上"]
console.log(arr.reduce((a,b)=>b.length>a.length ? b:a));
5、数组元素是对象
// 计算所有的价格总和
var arr = [
{ name: '口红', price: 180, count: 2 },
{ name: '围巾', price: 280, count: 1 },
{ name: '风衣', price: 380, count: 3 },
]
console.log(arr.reduce((a,b)=>a+b.price));
结果会很奇怪,因为没有默认值,单个数组元素默认值是第一个元素,对象不能作为默认值,无法进行相加,必须设置一个默认值
var arr = [
{ name: '口红', price: 180, count: 2 },
{ name: '围巾', price: 280, count: 1 },
{ name: '风衣', price: 380, count: 3 },
]
console.log(arr.reduce((a,b)=>a+b.price,0));
6、什么时候使用reduce:数组所有元素都需要进行参与