例子:要把大象放冰箱:
class:
类的继承extends:
class Father {}
class Son extends Father {}
子类构造函数中使用super():
继承属性或方法遵循:就近原则,虽然继承,但子类有的就不执行父类的,若想使用父类的同名方法,可以:super.同名方法()
类没有变量提升,先定义类,再实例化:
类中的属性和方法要使用this;
类中构造函数constructor的this:就是实例化后的对象,调用者;
类中普通函数的this:是这个方法的调用者;
es6之前没有类的概念,就是利用构造函数模拟类(写属性和方法),创建对象;
创建对象的方式:
new在执行时做的四件事:
构造函数内的成员:
构造函数中的属性和方法就是成员;(与类中的构造函数不同,类中的构造函数只写属性,方法用普通函数写)
实例成员:构造函数内部通过this添加的属性和方法;只能通过实例化的对象访问;
function Star(name, age) {
this.unmae = name;
this.age= age;
this.sing = function() {
console.log('aaa')
}
}
//var ldh = new Star('刘德华', 18)
console.log(ldh.uname)
静态成员:通过构造函数本身添加的成员;只能通过构造函数直接访问;
Star.sex = '男'
console.log(Star.sex)
没有类之前,使用构造函数的问题:
构造函数的原型:prototype对象
实例化对象的原型__proto__
实例化对象有个__proto__对象原型属性,这个属性跟构造函数的原型对象的指向是同一个对象;所以实例化对象可以访问构造函数原型上的方法;
lbh.__proto__ === Star.prototype
//proto、prototype都是系统自动添加的;
javascript成员查找规则:
构造函数以及原型对象中的this指向的都是实例化的对象;
es6 之前没有提供 extends继承,可以通过构造函数+原型模拟继承,也成为组合继承
call():直接调用函数;
fun.call(thisArg,arg1,arg2,...)
thisArg:当前调用函数this的指向对象
在es6之前,实现继承方式:借用父构造函数继承属性
function Father(uname,age) {
this.uname = name;
this.age = age;
}
function Son(uname,age){
Father.call(this,uname,age)//直接执行了父构造函数
}
var son = new Son('aa',12)
借用原型对象继承方法:
想要Son的实例继承父构造函数原型上的money方法;
让子构造函数的原型对象指向父构造函数实例;
Son.prototype = new Father(),这样就可以通过父构造函数实例的proto访问父构造函数原型对象上的方法money;
此时,再通过Son.prototype.exam = function(){},创建的方法就添加到当前这个对象father实例上了,不会影响父构造函数的原型对象;
但是现在Son的实例对象就不能指向Son构造函数的原型对象了,需要把
Son.prototype.constructor = Son;
构造函数特点:
es6通过类实现面向对象编程
es6新增的方法
遍历数组方法:forEach()、map()、filter()、some(只有some,利用return true可以终止循环,寻找唯一数组时效率高)、every()
var arr = [1,2,3];
var sum = 0;
arr.forEach(function(value,index,array){
console.log('每个数组元素'+value);
console.log('每个数组元素的索引'+index);
console.log('数组本身'+array);
sum +=value
})
console.log(sum)//6
filter():筛选数组元素,返回新数组,需要接收;
var arr = [11,22,33]
var newArr = arr.filter(function(value,index){
return value >=20;
})
console.log(newArr);//[22,33]
some():用于检测数组中元素是否满足某个条件,满足就返回true不再继续执行,否则返回false;
var arr = [10,20,30]
var flag = arr.some(function(value){
//return value > 20;//true
return value<3;//不直接返回true/false时就要返回条件,然后在外面接收;
})
console.log(flag);//false
filter、some,都是查找条件,filter将满足条件的所有元素返回到向新数组;some就是查看是否满足,返回true/false,并且中途满足了就不再向后执行;
查找数组中唯一元素时,some比较合适;利用return true就不用外面接收了;
data.some(function(value){
if(value.pname == product.value){
arr.push(value);//需要在方法内部把要做的事情做了
return true;
}//直接返回了true/false,就不需要外部接收了。
获取按钮dom元素:querySelector(按css选择器获取第一个),querySelectorAll()
var search_pro = document.querySelector('.search_pro')
监听按钮点击(绑定点击事件是onclick,按钮有点击事件不用绑定):
search_pro.addEventListener('click',function(){
var arr = [];
data.some(function(value){
if(value.pname == product.value){
arr.push(value);
return true;
}
});
setData(arr);
})
trim()方法,去除字符左右两边的空格,并返回新字符串;
var str1 = str.trim();
-------------------------------
var input = document.querySelector('input');
var btn = document.querySelector('botton');
var div = document.querySelector('div');
btn.onclick = function(){
var str=input.value.trim();
if(str === ''){alert('请输入内容')}
div.innerHTML=str;
}
es5新增的方法
对象方法:Object.defineProerty();定义新属性或者修改原有属性;
//Object.defineProperty(对象名,属性名,{对象配置/描述})
第一个参数:obj
第二个参数:prop
第三个参数配置:descriptor
{
value:999;//设置属性的值,默认undefined
writable:false;//设置属性的值是否可以重写,默认false,不可重写
enumerable:false;//设置属性是否可枚举,默认false,不可枚举
configurable:false;//设置属性是否可以被删除或者是否可以修改配置,默认false,不可删除不可配置;
}
console.log(Object.keys(obj));//["id","name"],age,不可枚举的不会遍历出来
三种定义方式:
//所有函数都是Function的实例,因为所有函数都有proto原型,所以所有函数都是对象
六种调用方式:
this的指向为调用者;(七种调用者就是七种指向情况)
改变this指向:
call():fn.call(obj,uname,age,sex)
作用:1、调用执行函数;2、改变this为指定对象;3、实现继承,因为有调用效果,在子构造函数中执行:
Father.call(this,uname,age,sex)
apply():fn.apply(obj,[参数,参数]):参数必须为数组
bind():var newfn = fn.bind()
应用场景:call经常做继承;apply经常与数组有关系;bind不调用函数,想改变this指向,例如定时器中使用,不立即执行函数;
特点
使用严格模式方式:
在脚本js中使用严格模式:
只在函数中使用严格模式:
function fn(){
'use strict';
}
严格模式:
变量作用域:全局变量(全局作用域)、局部变量(函数作用域)
闭包:指有权访问另一个函数作用域中变量的函数。:首先是一个函数,然后可以访问并使用别的函数不能访问的函数变量。
闭包作用:延长了变量生命周期,延伸了变量的作用范围;(这也造成了内存泄漏,闭包后不适用,变量就不销毁)
function fn(){
var num = 10;//局部变量本来在函数中使用完就销毁,不过闭包了还有函数要用,所以先不销毁;
return function(){//直接把要对局部变量的操作写在这,返回出去,让外部决定何时执行;
console.log(num)
}
}
var f = fn();
f();//变量num在这还要用,在这里使用完才销毁变量num;
//闭包实现点击li输出对应索引
for(var i = 0; i
含义:函数内部自己调用自己,就是递归;
注意:栈溢出问题!就是死循环;一定加return退出。
var num = 1;
function fn() {
console.log('打印六句话')
num +=1;
if(num == 6){
return;
}
fn();
}
浅拷贝:只是拷贝一层,更深层次对象级别的只拷贝引用;
浅拷贝,拷贝了键值对,如果值也是对象,就属于深一层,此时只会拷贝这个属性值的引用地址,所以修改也会原对象;
//浅拷贝方式一
var o = {}
for(var k in obj){
o[k] = obj[k];//只是拷贝了栈内存中的数据,包括堆内存地址;所以对象o更改堆内存的数据也会影响对象obj;栈内存中的改不了,变量赋值只是改变了自身的指向地址;
}
//浅拷贝方式二:es6语法
Object.assign(o,obj);
深拷贝:每一级别的数据都会拷贝;(利用递归)
思路:对每一层都拷贝到底,第一次对第一层拷贝;
就是判断这个属性的值是不是基本数据类型,如果是那就是简单赋值;
如果属性值是数组,就在属性名上新建一个空数组,调用递归,把数组的值拷贝过来;
如果是对象也类似,递归操作,深拷贝子对象;
//深拷贝对象obj:
var obj = {id:1,name:'andy',msg:{age:18},color:['white','black']};
var o = {};
function deepCopy(newObj, oldObj){//这个函数就是传的新对象、旧对象,所以递归完美!!!!!
for(var k in oldObj){
var item = oldObj[k];//获取要拷贝的对象的每个属性的属性值
-------------------------------------------
if(item instanceof Array){
newObj[k]=[];//就是给新对象创建一个数组属性:o.color=[]
deepCopy(newObj[k],item);//递归,深拷贝obj.color数组对象;将这个属性名的数组对象拷贝给了o.color属性;
------------------------------------------------------
else if(item instanceof Object){
newObj[k]={};
deepCopy(newObj[k],item);
}//要拷贝的属性值是对象,需要深拷贝内部属性,所以采用递归;将这个属性名的对象拷贝给了o.msg属性;
---------------------------------------------------
else{
newObj[k] = item;//这个属性名的值是简单数据类型,存在栈内存中,直接赋值即可,对象o修改这个属性也不会影响对象obj,只是改了自身的栈地址;
}
---------------------------------------
}
}
}
deepCopy(o,obj)
在js创建正则表达式的两种方式:
第一种:利用正则构造函数RegExp
var reg = new RegExp(具体表达式);//在构造函数中传入具体表达式
第二种:利用字面量创建正则表达式
var reg = /123/;//直接写具体表达式(意思是:包含123就行)
写好了正则表达式怎么使用:
利用test()方法验证字符串是否符合验证规则,将指定的字符放入表达式中验证;返回值为true/false
reg.test(‘abc’);//直接用正则表达式的test方法
正则表达式的组成:
可以由简单的字符构成;
可以是简单和特殊字符组合;
组成部分之----边界符:^、$
var reg = new RegExp(/^abc/);//以abc开头
var reg1 = new RegExp(/^abc$/);//又是开头又是结尾,精准为abc
reg.test('abc');//true
reg.test('aabc');//false
reg.test('abcabc');//true
-------------------------------
reg1.test('abc');//true
reg1.test('abcd');//false
reg1.test('abcabc');//false
组成部分之----字符类:[]、[-]、[^]
var reg = /[abc]/;//只要有abc中的其中一个都可以;
var reg1 = /^[abc]$/;//只能是abc中其中一个;
var reg2 = /^[a-z]$/;//只能是26个字母中的其中一个;
--------------------------------
reg.test('as');//true
reg1.test('a');//true
reg2.test('f');//true
------------------------
var reg = /^[a-zA-Z0-9_-]$/;//表示只能是26个大小写字母0-9的数字下划线短横线中其中一个,都可以;(现在还是只能输一个字符)
-----------------------------
/^[a-zA-Z]$/.test('a');//
量词符:设定某个模式出现的次数;/^a* / ; / [ a − z ] 3 , 16 /;/^[a-z]{3,16} /;/[a−z]3,16/
//验证手机号码表单步骤:
//第一步:先有用于验证的正则表达式,没有正则怎么验证:
var regtel = /^1[3|4|5|8][0-9]{9}$/;
//第二步:获取表单元素,用于提供要验证的内容:
var tel = document.querySelector('#tel');//通过input标签的id获取元素
//第三步就是验证(什么时候验证,验证后怎么办。):两种情况:true符合/false不符合:分别操作;
tel.onblur = function(){//什么时候验证:表单失去焦点的时候;
if(regtel.test(tel.value)){
tel.nextElementSibling.className = 'success';//下一个兄弟(提示栏)添加success类;
tel.nextElementSibling.innerHTML = '恭喜您输入正确!'
}
else{
tel.nextElementSibling.className = 'error';//下一个兄弟(提示栏)添加error类;
tel.nextElementSibling.innerHTML = '手机格式不正确!'
}
}
可以把正则验证封装到函数中,让外界执行这个函数时:传入正则表达式、表单元素;
regexp(tel, regtel);
function(el, reg){
el.onblur = function(){
if(reg.test(el.value)){}
else{}
}
}
stringObject.replace(regexp/substr被替换的内容,replacement用于替换的内容)
:返回值是一个替换完的字符串,所以要接收;
------------------------------------------------------
var str = 'andy和red';
var newStr = str.replace('andy','body');
var newStr = str.replace(/andy/,'body');//替换第一个andy;
正则表达式实现替换时的参数:/匹配内容|匹配内容/[switch]
正则表达式的验证、替换功能:
es:ECMAScript,是ECMA国际标准化组织制定的一项脚本语言的标准价规范;
2015.6:ES2015(ES6);2016.6:ES2016;…
es6就是泛指es2015及以后版本;
let:声明变量
特征一:使用let声明的变量具有块级作用域;
特征二:不具有变量提升;
必须先声明变量,才能使用let声明的变量;(变量提升其实是语言的bug,不好)
console.log(a);
let a = 1;//报错
特性三:暂时性死区;
var num = 100;
if(true){
console.log(num);//报错,因为let的暂时性死区特性,花括号内部只要使用let,相应的变量就会自动绑定这个括号里的,而不会去外部找全局变量(按常理是会向外找,但let声明的变量就不行),又因为没有变量提升,所以报错;
let num = 20;
}
面试题:
let arr = [];
for(let i = 0;i<2;i++){
arr[i] = function(){
console.log(i);
}
}
arr[0]();//0
arr[1]();//1
//let声明的变量会产生块级作用域,相当于:
{let i = 0;arr[0] = function(){console.log(i)}};
{let i = 1;arr[1] = function(){console.log(i)}};
//var声明,相当于:
var i = 2;
{arr[0] = function(){console.log(i)}};
{arr[1] = function(){console.log(i)}};
const:声明常量
常量,就是指向的地址不能发生改变;
因为基本数据类型就是在栈内存存储的不可变的值,所以指向基本数据类型的地址不改变数据就不会改变;
而指向堆内存中的是引用类型,当引用类型的内容改变时,但是地址并没有不变也符合常量要求,不会报错。
特性一:具有块级作用域;
特性二:声明常量时,必须赋初值;
特性三:声明常量为基本数据类型时,值不能修改;声明引用数据类型时,值可以修改,只要地址不变就行;
const arr = [100, 200];
arr[0] = 123;//正确;地址没有改变;
arr = [1, 2];//错误;修改了常量地址;
js解析引擎不需要实时监听常量变化,性能高;
按照一定模式,从数组或对象中提取值,将提取的值赋值给另外的变量;
[]:数组解构
let arr = [1,2,3];
let [a,b,c,d] = arr;
console.log(a);//1
console.log(d);//undefined
//变量数量与数组数量一一对应,
{}:对象解构
let person = {name:'zhangsan', age:18};
let { name,age } = person;
console.log(name);//zhangsan
console.log(age);//18
//变量名与对象变量名对应;匹配成功就赋值;
//想改名用冒号:{name:newName, age:newAge}
console.log(newName);//zhangsan
()=>{}:简化函数定义方式;类似:var fn = function(){};
如果函数体只有一句代码,花括号和return可以省略:(n1,n2)=> n1+n2;
如果形参只有一个,小括号可以省略:n1 => n1;
const fn = n1=>console.log(n1);
箭头函数的this指向:
不同于普通函数:指向调用者;
箭头函数this:指向函数定义位置的上下文(作用域:全局作用域、函数作用域);
function fn(){
console.log(this);
return ()=>{ console.log(this) }
}
const obj = { name:'zhangsan' }
fn.call(obj);//call方法直接执行函数,打印:{name:'zhangsan'};
//返回的箭头函数,还没有执行;
const resFn = fn.call(obj);
resFn();//{name:'zhangsan'}{name:'zhangsan'}
//不用箭头函数时,应该是指向window;但是箭头函数this指向函数定义时的位置,所以指向fn函数的调用者window,又函数被call改变了this,所以指向对象obj;
var age = 100;
var obj = {
age:20,
say:()=>{ alert(this.age) }
}
obj.say();//100
//箭头函数this:指向定义时的执行上下文(作用域);对象没有作用域,所以是全局作用域下的window;
使用一:**用于接收剩余函数参数:**当不确定函数会被传来多少参数,使用剩余参数,将所有参数以数组的形式接收;
const sum = (a,...args)=>{
console.log(args);//[2,3],1已经被a接收了;
}
sum(1,2,3);//不管传多少,都将没有被接收的参数,以数组形式保存给一个变量;使用的时候只需要对这个数组进行操作;
使用二:剩余参数与解构进行结合使用:
let arr = [1,2,3];
let [s1,...s2] = arr;
console.log(s1);//1
console.log(s2);//[2,3],接收剩余数据,并以数组形式保存;
let arr = ['a','b','c']
...arr;//'a','b','c';实际就是这个样子,把括号去掉,打印的时候逗号被编译成分隔符所以看不到;在下面数组合并的效果中可以解释;
console.log(...arr);//a b c
console.log('a','b','c');//a b c
用途:合并数组;push(…arr1);
利用扩展运算符将已知的多个数组扩展成序列,再放到新数组中;
push一个元素没问题,如果直接push数组,相当于二维数组了,所以用展开运算符;
let arr = ['a','b','c'];
let arr1 = ['d',1,2];
arr2 = [...arr,...arr1];
console.log(arr2);//[ 'a', 'b', 'c', 'd', 1, 2 ]
arr.push(...arr1);//[ 'a', 'b', 'c', 'd', 1, 2 ]
arr.push(arr1);//[ 'a', 'b', 'c', [ 'd', 1, 2 ] ]
扩展运算符与剩余参数:写法相似,作用不同;
利用扩展运算符:将伪数组展开再用 [] 包裹;
利用数组方法:Array.from(伪数组名);
var arrayLike = {
'0':'a',
'1':'b',
'2':'c',
'length':3
}
var arr = Array.from(arrayLike);
console.log(arr);//[ 'a', 'b', 'c' ]
find():查找数组中第一个满足条件的值,并返回值;
let newArr = arr.find(item=>item.id == 2)
findIndex():查找数组中第一个满足条件的值的索引,并返回索引;
let index = arr.findIndex(item=>item.id == 2)
includes():判断数组中是否包含某个值,返回布尔值;
let arr = ['a','b','c']
let result = arr.includes('a');
console.log(result);//true
第一个:模板字符串:可以解析变量;模板字符串内容可以换行写;可以调用函数。
`你好,${变量名}`;
`${fn()}`;//直接显示执行后的返回值,前提是函数执行有返回值
第二个:startsWith()、endsWith():判断字符串是否以某个字符开头或结尾;返回布尔值。
let r1 = str.startsWith('Hello');
console.log(r1);//true
第三个:repeat():表示将原字符串重复n次,返回一个新字符串;
'x'.repeat(3);//'xxx'
用途:用于存储数据,且重复的数据不会多次存放;
场景:浏览器记录用户搜索数据时,可以把用户输入的记录存到Set数据结构中,重复的记录只保存一次,节省空间。
Set数据结构怎么创建:
const s = new Set();
const s = new Set([1,2,3]);//初始化一些数据,因为类似数组,所以传入数组;
作用:做数组去重;
let arr = ['a','a','b',1,2,1];
const set = new Set(arr);
console.log(set.size);//4
//虽然set类似数组,但终究还是Set数据结构,不是真正的数组,所以要当做伪数组对待;利用扩展运算符转为真正数组;
const newArr = [...set];或者const newArr = Array.from(set);
console.log(newArr);//[ 'a', 'b', 1, 2 ]
add(value):添加值,并返回添加后;可以链式调用;
const s = new Set();
s.add('a').add('b');
delete(value);删除值,返回布尔值,表示是否删除成功;
has(value):返回布尔值,检查是否属于Set成员;
clear():清空成员,没有返回值。
有返回值想知道就要接收返回值;
遍历set数据结构:类似数组
const s = new Set([1,2,3]);
s.forEach(value=>{
console.log(value)
})