1.ECMAScript 是什么?
2.面向对象编程与面向过程编程区别
C语言是面向过程的编程,它的最重要特点是函数,通过主函数来调用一个个子函数。程序运行的顺序都是程序员决定好了的。C++、Java是面向对象的编程,类是它的主要特点,程序执行过程中,先由主函数进入,定义一些类,根据需要,执行类的成员函数,过程的概念被淡化了(实际上过程还是有的,就是主函数的那些语句),类就是对象,所以我们称之为面向对象程序设计。面向对象编程的三大特征:封装,继承,多态,封装是一种把代码和代码所操作的数据捆绑在一起,使这两者不受外界干扰和误用的机制.封装可被理解为一种用做保护的包装器,以防止代码和数据被包装器外部所定义的其他代码任意访问.对包装器内部代码与数据的访问通过一个明确定义的接口来控制.封装代码的好处是每个人都知道怎样访问代码,进而无需考虑实现细节就能直接使用它,同时不用担心不可预料的副作用. 在JAVA中,最基本的封装单元是类,一个类定义着将由一组对象所共享的行为(数据和代码).一个类的每个对象均包含它所定义的结构与行为,这些对象就好象是一个模子铸造出来的.所以对象也叫做类的实例. 在定义一个类时,需要指定构成该类的代码与数据.特别是,类所定义的对象叫做成员变量或实例变量.操作数据的代码叫做成员方法.方法定义怎样使用成员变量,这意味着类的行为和接口要由操作实例数据的方法来定义. 由于类的用途是封装复杂性,所以类的内部有隐藏实现复杂性的机制.所以JAVA中提供了私有和公有的访问模式,类的公有接口代表外部的用户应该知道或可以知道的每件东西.私有的方法数据只能通过该类的成员代码来访问.这就可以确保不会发生不希望的事情,继承是指一个对象从另一个对象中获得属性的过程.是面向对象程序设计的三大原则之二,它支持按层次分类的概念,新的子类继承其所有祖先的所有属性,多态是指一个方法只能有一个名称,但可以有许多形态,也就是程序中可以定义多个同名的方法,用"一个接口,多个方法"来描述.可以通过方法的参数和类型引用
3.实例
<script> class Star{ constructor(uname,age){ this.uname=uname; this.age=age; } sing(){ console.log(this.uname); } } var ldh=new Star("刘德华",22); ldh.sing(); </script>
运行结果是“刘德华”,注意:es6中必须先定义类再实例化对象,类里面公有的属性和方法必须要加this
4.构造函数和原型
1.使用构造函数创建对象
1.1概念
在典型的oop语言中,都存在类的概念,类就是对象的模板,但在es6之前,js中并没有引入类的概念,在es6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和它们的特征。
创建对象的三种方式:
1)对象字面量
2)new object()
3)自定义构造函数
1.2构造函数
构造函数是一种特殊的函数主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用,我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
new在执行时会做四件事情:
1)在内存中创建一个新的空对象
2)让this指向这个新的对象
3)执行构造函数里面的代码,给这个新对象添加属性和方法
4)返回这个属性和方法(构造函数里面不需要return)
1.3实例成员
实例成员就是构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问。
1.4静态成员
静态成员是在构造函数本身上添加的成员,例:Star.sex=‘男’,sex就是静态成员,静态成员只能通过构造函数来访问。
2.原型的作用
2.1函数原型
构造函数原型prototype,构造函数通过原型分配的函数是所有对象所共享的,JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,注意这个prototype就是一个对象,这个对象的所有属性和方法,都被构造函数所拥有。我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
2.2对象原型__proto__
对象都会有一个属性__proto__,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
对象原型(__proto__)和构造函数(prototype)原型对象里面都有一个constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。
3.访问对象成员的规则
3.1构造函数,原型对象,实例三者之间的关系
3.2原型对象this指向
在构造函数中,里面的this指向的是对象实例,原型对象里的this指向的是实例对象
4.扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法,比如给数组增加自定义求偶数和的功能。
<script> console.log(Array.prototype) Array.prototype.sum=function(){ var sum=0; for (var i=0;i<this.length;i++) { sum+=this[i]; } return sum; } var arr=[1,2,4]; console.log(arr.sum()); </script>
5.继承
es6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
5.1call()
调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg,arg1,arg2...)
thisArg 当前this的指向函数,
arg1,arg2 传递的其他参数
<script> function fn(x,y){ console.log(x+y); } var o={ name:"andy" }; fn.call(o,1,3); </script>
上述程序输出结果为4
5.2借用父构造函数继承属性
//父构造函数 function Father(name,age){ this.name=name; this.age=age; } //子构造函数 function Son(name,age){ Father.call(this,name,age); } var son=new Son("刘德华",18); console.log(son);
如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的原型对象
Son.prototype=new Father(); Son.prototype.constructor=Son;
5.3类的本质
1.类的本质其实还是一个函数,我们也可以简单的认为类就是构造函数的另外一种写法
2.类的所有方法都定义在类的prototype属性上
3.类创建的实例,里面也有__proto__指向类的prototype原型对象
4.es6的类其实就是语法糖
ps:语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。
6.es5中新增方法
6.1数组方法
6.1.1迭代方法
forEach(),map(),filter(),some(),every()
array.forEach(function(currentValue,index,arr))//数组元素,索引号,数组本身
filter方法用于创造一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组。
<script> var arr=[22,324,545,23,56]; var newArr=arr.filter(function(obj,index){ return obj>=30; }); alert(newArr) </script>
some方法用于检测数组中的元素是否满足指定条件,通俗点查找数组中是否有满足条件的元素,如果找到第一个满足条件的元素则终止循环,不再继续查找。
<script> var arr=[22,324,545,23,56]; var flag=arr.some(function(value){ return value>=50; }); alert(flag); </script>
注意:在forEach和filter里面return不会终止迭代
6.2字符串方法
1.trim()
会从一个字符串两端删除空白字符,trim()方法并不影响字符串本身,它返回的是一个新的字符串。
6.3对象方法
1.Object.defineProperty()
定义对象中新属性或修改原有的属性
Object.defineProperty(obj,prop,descirptor)
三个参数都是必须写的,obj是对象,prop是属性,descriptor是说明
第三个参数descriptor是以对象形式书写,
value:设置属性的值,默认为undefined
writable:值是否可以重写 true|false 默认为false
enumerable:目标属性是否可以被枚举 true|false 默认为false
configurable:目标属性是否可以被删除或是否可以再次修改特性 true|false 默认为false
<script> var Star={ name:"张三", age:22, sex:"男" }; Object.defineProperty(Star,"name",{ value:"李四" }); console.log(Star); </script>
7.函数进阶
7.1函数的定义方式
1)函数声明方式function关键字(命名函数)
function ss(){}
2)函数表达式(匿名函数)
var fun=function(){}
3)利用new Function(‘参数1’,‘参数2’,‘函数体’)
var ff=new Function('a','b','console.log(a+b)');
4)所有函数都是Function的实例
7.2函数的调用方式
1)普通函数
function fn(){ console.log("人生的巅峰"); } fn();
2)对象的方法
var o={ sayHi:function(){ console.log("人生的巅峰"); } } o.sayHi();
3)构造函数
function Star(){}; new Star();
4)绑定事件函数
点击了按钮就可以调用这个函数
btn.onclick=function(){}
5)定时器函数
这个函数是定时器每隔一秒中自动调用一次
setInterval(function(){},1000)
6)立即执行函数
这种函数不需要调用,会自动执行
(function(){ console.log("人生的巅峰"); })();
7.3this
1.this指向
调用方式 | this指向 |
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 |
window |
2.改变函数内部this指向
1)call()
fun.call(thisArg,arg1,arg2...)
2)apply()
fun.apply(thisArg,[argsArray])
argsArray传递的值必须包含在数组里面
function fn(arr){ console.log(arr); } var o={ sayHi:function(){ console.log("人生的巅峰"); } } fn.apply(o,['pink']);
apply()的应用
············var arr=[1,4,43,23,56,2,3];//求最大值 var max=Math.max.apply(null,arr); alert(max)
3)bind()
bind不会调用方法,但是能够改变函数内部this指向
fun.bind(thisArg,arg1,arg2,...)
var c={ name:"zs" }; function fn1(){ console.log(this); } var a=fn1.bind(c); a();
4)call,apply,bind总结
相同点:都可以改变函数内部this指向
不同点:1.call和apply会调用函数,并且改变函数内部this指向,2.call和apply传递的参数不一样,call传递参数为arg1,arg2,apply传递参数为数组,3.bind不会调用函数,可以改变函数内部this指向。
主要应用场景:1.call经常做继承。2.apply经常跟数组有关系,比如借助数学对象实现数组最大值与最小值。3.bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
7.4严格模式
7.4.1概论
严格模式对正常的JavaScript做了一些更改
1)消除了JavaScript语法中一些不严谨、不合理之处,减少了一些怪异行为。
2)消除代码运行的一些不安全之处,保证代码运行的安全。
3)提高编译器效率,增加运行速度。
4)禁用了在ECMAScript的未来版本中可能会定义一些语法,为未来新版本的JavaScript做好铺垫,比如一些保留字,如:class,enum,export,extends,import,super不能做变量名
7.4.2开启严格模式
严格模式可以运用到整个脚本或个别函数中,因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
在JavaScript内加入"use strict";这句话即可
加入严格模式后
1.this指向问题
1)我们的变量必须先声明再使用
2)我们不能随意删除已经声明好的变量
3)严格模式下全局作用域中函数中的this是undefined
4)严格模式下,如果构造函数不加new调用,this会报错
5)定时器this还是指向的是window
2.函数变化
1)不能有重名的参数
2)函数必须声明在顶层新版本的JavaScript会引入“块级作用域”,为了与新版本接轨,不允许在非函数的代码块内声明函数。
7.5高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
function fn(a,b,callback){ console.log(a+b); callback&&callback(); } fn(1,2,function(){ console.log("我是高阶函数被调用了"); });
function fn(){ return function(){}; } fn();
7.6闭包
7.6.1变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量
1.函数内部可以使用全局变量
2.函数外部不可使用局部变量
3.当函数执行完毕之后,本作用域内的局部变量会销毁
7.6.2什么是闭包
闭包是指有权访问另一个函数作用域中变量的函数,简单的说是 ,一个作用域可以访问另外一个函数内部的局部变量。
function fn1(){ var num=10; function fun(){ console.log(num); } fun(); } fn1();
外部作用域访问内部局部变量
function fn(){ var num=10; function fun(){ console.log(num); } return fun; } var f=fn(); f();
闭包的主要作用:延伸了变量的作用范围
立即执行函数也称为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
7.7递归
7.7.1什么是递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。 简而言之:函数内部自己调用自己。递归很容易发生栈溢出的错误,所以必须要加退出条件return
7.7.2浅拷贝和深拷贝
1)浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
2)深拷贝拷贝多层,每一级别的数据都会拷贝
浅拷贝的两种方法
var o={ id:1, name:"andy" }; var c={}; for(var i in o){ c[i]=o[i]; } console.log(c);
var o={ id:1, name:"andy" }; var c={}; Object.assign(c,o); console.log(c);
8.es6新增语法
8.1let关键字
let是es6中新增的用于声明变量的关键字,let声明的变量只在所处于的块级有效
if(true){ let a=10; alert(a); }
注意:使用let声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性
使用let声明的变量没有变量提升
console.log(a);
let a=10;
暂时性死区
var num=10; if(true){ console.log(num); let num=20; }
8.2const关键字
作用:声明常量,常量就是值(内存地址)不再变化的量。
使用const关键字声明的常量具有块级作用域。
if(true){ const a=10; console.log(a);//10 } console.log(a);//undefined
使用const关键字声明的常量必须赋初始值
let,const,var的区别
1)使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
2)使用let声明的变量,其作用域为该语句所在代码块内,不存在变量提升。
3)使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。
8.3解构赋值
es6允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构。
8.3.1数组解构
数组解构允许我们从数组中一一提取值,然后赋值给变量,例:
let [a,b,c]=[1,2,3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
如果没有对应的值则为undefined
8.3.2对象解构
对象解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量
let person={name:"张三",age:21}; let {name,age}=person; console.log(name);//张三 console.log(age); //21
另一种写法
let person={name:"张三",age:21}; let {name:myname}=person; console.log(myname);//张三
8.4箭头函数
箭头函数是es6中新增函数定义的方式()=>{ }
const fn=()=>{ console.log("我是箭头函数"); } fn();
在箭头函数中,如果函数体中只有一句代码,并且代码的执行结果就是代码的返回值,函数体大括号可以省略,例如:
const sum=(n1,n2)=>n1+n2;
在箭头函数中,如果形参只有一个,形参外侧的小括号也是可以省略的
const f=v=>{
alert(v);
}
f(20);
箭头函数中不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
8.5剩余参数
当函数实参个数大于形参个数时,剩余函数允许我们将一个不定数量的参数表示为一个数组
const sum=(...args)=>{ let total=0; args.forEach(item=>total+=item); return total; }
sum(20,30);
剩余参数和解构配合使用
let student=['zhangsan','lisi','wangwu']; let [s1,...s2]=student; console.log(s1);//'zhangsan' console.log(s2);//['lisi','wangwu']
8.6Array的扩展方法
8.6.1分隔数组
扩展运算符可以将数组或对象转为用逗号分隔的参数序列
let arr=['a','b','c']; console.log(...arr); //a b c console.log('a','b','c');//a b c
8.6.2合并数组
扩展运算符可以应用于合并数组
方法一:
let arr1=['a','b','c']; let arr2=['a','b','c']; let arr3=[...arr1,...arr2]; console.log(arr3);
方法二:
let arr1=['a','b','c']; let arr2=['a','b','c']; arr1.push(...arr2); console.log(arr1);
8.6.3转换数组
扩展运算符可将类数组或可遍历对象转换为真正的数组
let arrs = document.getElementsByTagName('div'); let arrays = [...arrs]; console.log(arrays);
8.6.4Array实例方法
Array.from(),必须要加上length才能转换,例:
var arr={ "0":"1", "1":"2", "length":2 }; var newArr=Array.from(arr,item=>item*2); console.log(newArr);//[2,4]
Array.find(),查找数组中第一个满足条件的元素,如果查到没有,则返回undefined,例:
var arr=[{ id:1, name:'zhangsan' },{ id:2, name:'lisi' }]; let target=arr.find(item=>item.id==2); console.log(target);
Array.findIndex(),用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
var array=[10,15,20]; let index=array.findIndex(item=>item>10); alert(index);//1
includes(),用于判断元素是否在数组里面,返回值Boolean类型的
var array=[10,15,20]; let flag=array.includes(10); alert(flag);//true
8.7String的扩展方法
1)模板字符串
模板字符串中可以解析变量
var name='张三';
let sentence=`my name is ${name}`;
console.log(sentence);
模板字符串可以调用函数
const fn=()=>"我是fn函数"; let html=`我是模板字符串,${fn()}`; console.log(html);
2)startsWith()和endsWith()
startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
let str="hello world"; var flag1=str.startsWith("he");//true var flag2=str.endsWith("he");//false
3)repeat()
repeat方法表示将原字符串重复n次,返回一个新字符串
var ss="x".repeat(3);
9.es6内置对面扩展
9.1set数据结构
es6提供了新的数据结构set,它类似于数组,但是成员的值都是唯一的,没有重复的值。
set本身是一个构造函数,用来声明set数据结构
const s=new Set();
set数据结构可以接受一个数组作为参数,用来初始化,可以用来数组去重。
const set=new Set(["a","a","b","b"]); const ary=[...set]; console.log(ary);
实例方法:
1)add(value),添加某个值,返回set结构本身
2)delete(value),删除某个值,返回一个Boolean值,表示删除是否成功
3)has(value),返回一个Boolean值,表示该值是否为Set成员
4)clear(),清除所有成员,没有返回值
遍历
set数据结构和数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值
const set=new Set(["a","a","b","b"]); set.forEach(value=>console.log(value));