创建类
语法:
class name{
//class body
}
创建实例:
var xx = new name();
注意:类必须使用new实例化对象
类constructor构造函数
constructor()方法是类的构造函数(默认方法),用于传递参数,返回实力对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
继承
现实中的继承:子承父业,比如我们继承父亲的姓。
程序中的继承:子类可以继承父类的一写属性和方法。
语法:
class Father{//父类
}
class son extends Father{//子类继承父类
}
super关键字
**super()**用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
class Father {
say(){
return"我是爸爸";
}
}
class son extends Father{//这样子类就继承了父类的属性和方法
say(){
//super.say() super调用父类方法
return super.say()+'的儿子';
//super.say() 就是调用父类中的普通函数say()
}
}
var damao = new son();
console.log(damao.say());
class Person{ //父类
constructor (surname){
this.surname = surname;
}
}
class Student extends Person{ //子类继承父类
constructor(surname,firstname){
super(surname); //调用父类的constructor(surname)
this.firstname = firstname; //定义子类独有的属性
}
}
继承中的属性或者方法查找原则:就近原则
注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类构造方法)
三个注意点:
构造函数是一种特殊函数,主要用来初始化对象,即为对象成员变量赋初值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在js中使用构造函数时要注意一下两点:
1.构造函数创建某一类对象,其首字母要大写
2.构造函数要和new一起使用才有意义
js中的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两种方式添加的成员,就分别称为静态成员和实例成员。
静态成员:在构造函数本身上添加的成元称为静态成员,只能由构造函数本身来访问
Star.sex='男' //静态成员
实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问。
function Star(uname,age){
this.uname=uname; //实例成员
this.age=age; //实例成员
this.sing=function(){
console.log("唱歌")
} //实例成员
}
构造函数的问题
构造函数方法很好用,但是存在浪费内存的问题
在创建复杂数据类型(如:function方法),会单独开辟内存空间来存放这个方法,每创建一个实例对象都会开辟一个空间来存放同一个方法,非常浪费内存和时间
构造函数原型prototype
构造函数通过原型分配的函数时所有对象所共享的
js规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有的对象实例就可以共享这些方法
Star.prototype.sing = function(){
console.log('唱歌'); //调用:对象实例.sing()
}
对象的原型 __proto__
每一个对象都会有一个属性__proto__
指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型的存在。
__proto__
对象的原型和原型对象prototype是等价的(对象实例的__proto__
对象的原型指向构造函数的原型对象prototype)
调用方法的查找规则:(以sing方法为例)首先看对象本身有没有sing方法,如果有,就执行对象自己本身的sing方法,如果对象本身没有sing这个方法,因为所有__proto__
的存在,就去构造函数原型对象prototype身上去查找sing这个方法。
constructor构造函数
对象原型(__proto__
)和构造函数(prototype)原型对象里面都有一个constructor属性,constructor我们成为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用于那个构造函数,它可以用让原型对象重新指向原来的构造函数,很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数(例如:我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数)
js的成员查找机制(规则)
__proto__
指向的prototype原型对象)。__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。原型链
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3W5pemOS-1584628577581)(C:\Users\李善炳\Desktop\原型链.png)]
原型对象this指向
this指向函数调用者(对象实例)
扩展内置对象
可以通过原型对象,对原来的内置对象进行自定义的方法。比如给数组增加自定义求偶数和的功能。
例:给Array扩展求和的方法
Array.prototype.sum = function(){
var sum = 0;
for(var i = 0; i < this.length; i++){
sun += this[i];
}
}
**注意:**数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {}
,只能是Array.prototype.xxx =function(){}
的方式
es6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承没被成为组合继承。
call()
可以调用函数(fun.call()
),并且修改函数运行时的this指向
fun.call(thisArg, arg1, arg2, ...)
借用构造函数继承父类型属性
核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性
function Father(uname, age){
//this指向父构造函数的对象实例
this.uname = uname,
this.age = age,
}
function Son(uname, age, score){
//this指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('小明', 18, 100);
console.log(son) //son实例中有uname,age,scpre属性,uname和age是从Father继承来的,score是自己独有的
借用原型对象继承方法
Son.prototype = Father.prototype //这样赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father(); //将父构造函数实例化赋值给子原型对象,但是这样Son.prototype.constructor就指向了Father构造函数。
Son.prototype.constructor = Son;//使Son原型对象的constructor指回原来的构造函数
__proto__
指向类的原型对象数组方法
迭代(遍历)方法:forEach()、map()、filter()、some()、every();
forEach()
array.forEach(function(currentValue, index, arr){})
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身
var arr = [1, 2, 3];
arr.forEach(function(currentValue, index, arr){
console.log(currentValue);//输出每个数组元素
console.log(index);//输出每个数组元素的索引号
console.log(arr);//输出这个数组
})
filter()
array.filter(function(currentValue, index, arr){})
例:
var arr = [12, 66, 88];
var newArry = arr.filter(function(value, index){
return value>20;//返回大于20的数组元素,组成一个新的数组
});
console.log(newArry);//输出由66和88组成的数组
some()
array.some(function(currentValue, index, arr){})
true
,如果查找不到就返回false
例:
var arr=[10, 30, 4];
var flag = arr.some(function(value, index){
return value>20;
})
console.log(flag);//输出true
字符串方法
trim()方法会从一个字符串的两端删除空白字符
str.trim()
trim()并不影响元字符串本身,它返回的是一个新字符串
对象方法
Object.defineProperty()定义对象中新属性或修改原有的属性
Object.defineProperty(obj, prop, descriptor)
Object.keys()用于获取对象自身所有属性
Object.keys(obj)
函数的定义
函数声明方式function关键字(命名函数)
函数表达式(匿名函数)
new Function(‘参数1’, ‘参数2’, ‘函数体’)
var fu = new Function('参数1', '参数2'..., '函数体')
Function里面参数都必须是字符串格式
函数的调用
函数内this的指向
这些this的指向,是当我们调用函数的时候确定的,调用方式的不同决定了this的指向不同
一般指向函数的调用者
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象,原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部this指向
js提供了一些函数方法帮助处理函数内部this的指向问题,常用的有bind()、call()、apply()方法。
call方法
call方法调用一个对象。简单理解为调用函数的方式,但是他可以改变函数的this指向。
fun.call(thisArg, arg1, arg2,...)
apply()方法
apply()方法调用一个函数。简单理解为调用函数的方式,但是它也可以改变函数的this指向
fun.apply(thisArg, [argsArray])
thisArg:在fun函数运行时指定this的值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
我们可以利用apply借助于数学内置对象求最大值最小值以前其他操作
var arr = [1, 66, 3, 99, 4];
var max = Math.max.apply(Math, arr);
console.log(max)//输出99
bind()方法
bind()方法不会调用函数,但是能改变函数内部this指向
fun.bind(thisArg, arg1, arg2, ...)
thisArg:在fun函数运行时指定this的值
arg1,arg2:传递其他参数
返回由指定this值和初始化参数改造的原函数拷贝
var o = {
name:'andy'
};
function fn(){
console.log(this);
console.log(a + b);
}
var f = fn.bind(o, 1, 2);//使fn函数的this指向o对象
f();//调用函数
call、apply、bind总结
**相同点:**都可以改变函数内部this指向
区别:
主要应用场景:
js除了提供正常模式外,还提供了严格模式(strict mode)。Es5的严格模式是采用具有限制性js变体的一种方式,即在严格条件下运行js代码。
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的JavaScript语义做了一些更改:
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;或
<script>
'use strict';
</script>
//或
<script>
(function(){
'use strict';
})()
</script>
因为“use strict”加了引号,所以老版本的浏览器会把它当做一行普通字符串忽略
为函数开启严格模式
给某个函数开启严格模式,需要把"use strict";(或’use strict’;)声明放在函数体所有语句之前。
<script>
function fn(){
'use strict';
//该函数里面的代码按照严格模式执行
//该函数外的代码还是按照普通模式执行
}
function fun(){
//该函数里面的代码按照普通模式执行
}
</script>
严格模对JavaScript的的语法和行为,都做了一些改变。
高阶函数是对其他函数进行操作的函数,他接收函数作为参数或将函数作为返回值输出。
函数也是一种数据类型,同样可以作为参数,传递给另一个函数做参数使用。最典型的就是作为回调函数。
变量根据作用域不同分为两种,全局变量和局部变量。
闭包(closure)是指有权访问另一个作用域中变量的函数
简单理解就是,一个作用域可以访问另一个函数内部的局部变量。
如果一个函数内部可以调用起本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己,这个函数就是递归函数
由于递归很容易发生“栈溢出”(stack overflow)错误,所以必须要加退出条件return。
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
var o = {};
var obj = {
id:1,
name:'andy',
msg:{
age:18,
}//该对象属性只会被拷贝地址
};
for(var k in obj){
o[k] = obj[k];
}
Object.assign(o, obj);//将obj拷贝给o
深拷贝拷贝多层,每一级别的数据都会被拷贝
var o = {};
var obj = {
id:1,
name:'andy',
msg:{
age:18,
}
}
function deepCopy(newObj, oldObj){
for(var k in oldObj){
var item = oldObj[k];
if(item instanceof Array){
//判断这个值是否是数组
newObj[k] = [];
deepCopy(newObj[k], item)
}else if(item instanceof Object){
//判断这个值是否是对象
newObj[k] = {};
deepCopy(newObj[k], item)
}else{
//这个值属于简单数据类型
newObj[k] = item;
}
}
}
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中正则表达式也是对象。
正则表达式通常用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名只能输入英文字母、数字或者下划线,昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用语过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
正则表达式的特点:
创建正则表达式
在JavaScript中,可以通过两种方式创建一个正则表达式。
通过调用RegExp对象的构造函数创建
var 变量名 = new RegExp(/表达式/);
通过字面量创建
var 变量名 = /表达式/;
测试正则表达式 test
test() 正则对象方法,用于检测字符串是否符合该规则,该对象返回true或false,其参数是测试字符串。
regxObj.test(str)
正则表达式的组成
一个正则表达式可以由简单的字符构成,比如/abc/,也可以是简单和特殊字符的组合,比如/ab*c/。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^、$、+等。
边界符
正则表达式中的边界符(位置符)用来提示字符所处位置,主要有两个字符
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
如果^和$在一起,表示必须是精确匹配。
var rg = /abc/;
console.log(rg.test('abc'));//返回true
console.log(rg.test('abcd'));//返回true
console.log(rg.test('aabcd'));//返回true
var reg = /^abc/;
console.log(reg.test('abc'));//返回true
console.log(reg.test('abcd'));//返回true
console.log(reg.test('aabcd'));//返回false
var reg1 = /^abc$/;
console.log(reg1.test('abc'));//返回true
console.log(reg1.test('abcd'));//返回false
console.log(reg1.test('aabcd'));//返回false
console.log(reg1.test('abcabc'));//返回false
字符类
字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内
字符组合:/^[a-z0-9]$/
[-]
方括号内部范围符 -
如果中括号里有 ^
表示取反的意思
var rg = /[abc]/;//只要包含有a、b、c其中一个,都返回true
var rg1 = /^[abc]$/;//三选一,只有是a、b、c其中一个的时候才返回true
var rg2 = /^[a-z]$/;//多选一,26个中任何一个小写字母都返回true
var rg3 = /^[a-zA-Z0-9_-]$/;//26个字母中任何一个字母、十个数字中的任何一个以及短横线、下划线,都返回true,仍然是多选一
量词符
量词符用来设定某个模式出现的次数。
量词 | 说明 |
---|---|
* | 重复0次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
括号总结
预定义类
预定义类指的是某些常见模式的简写方式
预定类 | 说明 |
---|---|
\d | 匹配0-9之间的任一数字,相当于[0-9] |
\D | 匹配所有0-9以外的字符,相当于[^0-9] |
\w | 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_] |
\W | 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_] |
\s | 匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f] |
\S | 匹配非空格的字符,相当于[^\t\r\n\v\f] |
replace替换
replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
stringObiect.replace(regexp/substr, replacement)
正则表达式参数
/表达式/[switch]
//例:
/表达式/gi
switch(也称为修饰符)按照什么样的模式来匹配,有三种值:
ES6的全称是ECMAScript,它是由ECMA国际化标准组织制定的一项脚本语言的标准化规范。
ES6中新增的用于声明变量的关键字
let声明的变量只在所处于的块级有效(一个大括号 )
if(true){
let a = 10;
}
console.log(a);//a is not defined
注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性
不存在变量提升(只能先声明再使用)
console.log(a);//a is not defined
let a = 20;
暂时性死区
var tmp = 123;
if(true) {
tmp = 'abc';//tmp is not defined
let tmp;
}
作用:声明常量,常量就是值(内存地址)不能变化的量
具有块级作用域
if(true) {
const a = 10;
}
console.log(a); //a is not defined
声明常量是必须赋值
const PI; //missing initializer in const declaration
常量赋值后,值不能更改
const PI = 3.14;
PI = 10;//Assignment to constant variable
const arr = [100, 200];
arr[0] = 1;
console.log(arr)//输出[1, 200];
arr = [1, 2];
console.log(arr);//Assignment to constant variable
var | let | const |
---|---|---|
函数级作用域 | 块级作用域 | 块级作用域 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
解构赋值
ES6中允许从数组中提取值,按照对应位置,对应变量赋值。对象也可以实现解构。
数组解构
数组解构允许我们按照一一对应的关系从数组中提取值然后将值赋值给变量
let arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(a);
console.log(b);
console.log(c);
let [a, b, c] = [1, 2, 3];
console.log(a);
console.log(b);
console.log(c);
如果解构不成功,变量没有对应的值,则该变量为undefined。
let [foo] = [];
console.log(foo);//输出undefined
let [bar, f] = [1];
console.log(bar);//输出1
console.log(f);//输出undefined
对象解构
对象解构允许我们使用变量的名字匹配对象的属性 匹配成功将对象属性的值赋值给变量
let person = {
name:'张三',
age:18
};
let {name, age} = person;
console.log(name);// 张三
console.log(age);// 18
let{name:myname, age:myage} = person;
//上面的语句相当于
//myname = person.name;
//myage = person.age;
console.log(myname);//张三
console.log(myage);//18
箭头函数
ES6中新增的定义函数的方式
箭头函数中没有arguments
(形参1,形参2)=>{
//函数体
}
const fn = ()=>{}
在箭头函数中,如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值,函数体的大括号可以省略
const sum = (num1, num2) => num1 + num2;
console.log(sum(10, 2));//输出12
在箭头函数中,如果形参只有一个,可以省略小括号
const result = n => n + 1;
console.log(result(5));//输出6
箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置中的this
const obj = {
name:'张三'
};
function fn(){
console.log(this);
return () => {
console.log(this);
}
}
fun = fn.call(obj);
fun();//两个输出都是obj对象
剩余参数(弥补箭头函数中没有arguments的不足)
剩余参数语法允许我们将一个不定数量的参数表示为一个数组
function sum(first, ...args){//args前面加三个.代表剩余的实参由args接收,以数组形式存储
console.log(first);// 10
console.log(args);// [20, 30]
}
sum(10, 20, 30);
剩余参数和解构配合使用
let students = ['张三', '李四', '王五'];
let [s1, ...s2] = students;
console.log(s1);//输出'张三'
console.log(s2);//输出['李四', '王五']
扩展运算符(展开语法)
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。
let arr = [1, 2, 3];
...arrl;//1, 2, 3
console.log(...arr); // 输出1 2 3 逗号被当做console.log的参数分隔符
扩展运算符可以应用于合并数组。
//方法一
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [...arr1, ...arr2];
console.log(arr3);//输出[1, 2, 3, 4, 5, 6]
//方法二
let arr4 = arr1.push(...arr2);
console.log(arr4);//输出[1, 2, 3, 4, 5, 6]
将类数组或可遍历对象转换为真正的数组
let divs = document.querySelectorAll('div');//获取页面中的所有div,以伪数组的形式存储
let arr = [...divs];//将伪数组转换为真正的数组
构造函数方法:Array.from()
将类数组或可遍历对象转换为真正的数组
let arr = {
'0':'a',
'1':'b',
'2':'c',
length:3
};
let newArr = Array.from(arr);//['a', 'b', 'c']
方法还可以接受第二个参数,作用类似于map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arr = {
'0':1,
'1':2,
'length':2
}
let newArr = Array.from(arr, item => item * 2);//将每个数组元素乘2
console.log(newArr);//输出[2, 4]
实例方法:find()(接收一个函数做参数:查找条件)
用于找出第一个符合条件的数组成员,如果没有找到返回undefined
var arr =[{
id:1,
name:'张三'
},{
id:2,
name:'李四'
}];
var newArr = arr.find(item => item.id==1);
console.log(newArr);//输出{id:1, name:'张三'}
实例方法:findIndex()
用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
let arr = [1, 5, 6, 9];
let index = arr.findIndex(value => value>5);
console.log(index);//输出2
实例方法:includes()
判断某个数组是否包含给定的值,返回布尔值
[1, 2, 3].includes(3);//true
[1, 2, 3].includes(4);//false
模板字符串
ES6新增的创建字符串的方式,使用反引号定义
let name = `amazing`;
模板字符串中可以解析变量。
let name = `张三`;
let say = `你好,我叫${name}`;
console.log(say);//输出:你好,我叫张三
模板字符串中可以换行
let result = {
name:'张三',
age:18,
sex:'男'
}
let html = `
${result.name}
${result.age}
${result.sex}
`;
模板字符串中可以调用函数
const say = function(){
return '即使遍体鳞伤,也要护你周全';
};
let print = `${say()},仅此而已`;
console.log(print);//输出:即使遍体鳞伤,也要护你周全,仅此而已
实例方法:startsWith()和endsWith()
let str = 'Hellow world!';
str.startsWith('Hellow');//true
str.endsWith('!');//true
实例方法:repeat() (参数为需要重复的次数)
repeat方法表示将原字符串重复n次,返回一个新字符串。
'x'.repeat(3);///"xxx"
'hello'.repeat(2);//hellohello
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
Set本身是一个构造函数,用来生成Set数据结构。
const s = new Set();
Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 3, 5]);
console.log(set);//{1, 2, 3, 5}
利用Set给数组去重。
let arr = [1, 1, 2, 4, 7, 4];
let set = new Set(arr);
let arr1 = [...set];
console.log(arr1);//输出数组:[1, 2, 4, 7]
实例方法:
const s = new Set();
s.add(1).add(2).add(3);//向Set结构中添加值
s.delete(2);//删除Set结构中的2值
s.has(1);//表示Set结构中是否含有1这个值,返回布尔值
s.clear();//将Set结构清空
遍历Set结构
Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作没有返回值。
let s = new Set(['a', 'b', 'c', 'd'])
s.forEach(value => console.log(value));//分别输出字符'a' 'b' 'c' 'd'