JS高级
JS AD Day 01
面向对象编程OOP:封装、继承、多态
使用功能来划分问题,而不是步骤。
类和对象
js中 对象是一组无序的相关属性和方法的集合,所有的事物都是对象。例如字符串,函数,数组等。
对象是由属性和方法组成的。
属性是事物的特征。
方法是事物的行为。
抽取对象公用的属性和行为组织成一个类。即抽象对象共有属性和行为封装成一个模板。
对类进行实例化,获取类的对象。
类,class,类抽象了对象的公共部分,泛指某一大类。
对象特指某一个,通过类实例化一个具体的对象。
1 .创建类
语法: 首字母大写
class MyClassName {
//class body
}
创建实例:
var xxx = new MyClassName();
类必须使用 new 来实例化对象。
constructor 构造函数
constructor()方法是类的构造函数(默认方法)。用于传递参数,返回实例对象,通过new命令生成实例对象时,自动调用该方法。如果不写也会自动生成。
例:
class Star {
constructor(uname,age) {
this.uname = uname;
this.age = age;
}
//注意 此处上下文分隔不同函数 没有加,
//且 写函数不用加function
say() {
console.log('hi');
}
}
var ldh = new Star('刘德华',18);
console.log(ldh.uname);
注意点
1)class关键字创建类,类名首字母大写
2)类里面有一个constructor函数,可接收传递过来的参数,同时返回实例对象
3)使用new关键字时,自动调用constructor,不写也会自动生。
4)创建类,类名后面不能加(),生成实例,类名后面可以加小括号。
5)构造函数不需要加function,类中所有函数都不用加function关键字,且多个函数方法间不用加’,'来进行分隔。
继承extends
extends ,子类可以继承父类的一些属性和方法。
class Father {
money() {
console.log(100);
}
}
class Son extends Father {
}
var son = new Son();
son.money();
super关键字
用于访问和调用父类上的函数。可以调用父类的构造函数,也可调用普通函数。
class Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x,y) {
super (x,y)//调用了父类中的构造函数
}
say(){
//调用了父类中的普通函数
console.log(super.say()+'的儿子')
}
}
var son = new Son(1,2);
son.sum();
super关键字可以调用父级构造函数也可以调用普通函数。
extends 继承就近原则 ,父亲和儿子 产生冲突的时候,离谁近先执行谁的。
如果要输出父类中的方法,加super.say(),super.say()就是调用父类中的普通函数say();
在子类构造中使用super,必须放在子类的this之前调用。
类和对象三个注意点
1 .es6中,类没有变量提升,所以必须先定义类才能通过类来实例化对象。
2 .类里面共有的属性和方法一定要加this使用。
3 .constructor里面的this指向的是创建的实例对象。类中的方法指向的是调用者,谁调用指向谁。如一个btn
//可以直接在constructor中调用方法,这样只要new一下,就直接执行
class MyName{
constructor(x,y){
this.x = x
this.y = y
this.say()//注意加this
}
say() {
console.log('sing a song')
}
}
new MyName
TAB栏切换案例
把整个tab栏表单当作一个对象,先抽象出来类,然后new一个出来。各种方法如新建切换删除等,写在类中,然后在init方法中注册。
insertAdjacentHTML
insertAdjacentHTML(positon,text); 将指定的文本解析为HTML或者XML,并将结果节点插入到指定位置。
一个 DOMString
,表示插入内容相对于元素的位置,并且必须是以下字符串之一:
'beforebegin'
:元素自身的前面。'afterbegin'
:插入元素内部的第一个子节点之前。'beforeend'
:插入元素内部的最后一个子节点之后。'afterend'
:元素自身的后面。
appendChild 不支持字符串,insertAdjacentHTML支持。
双击事件 :ondbclick
双击默认操作是选中文字,所以用以下代码清除默认操作:
window.getSelection?window.getSelection().removeAllRanges():document.selection.empty();
核心思路,双击文字的时候,在里面生成一个文本框,当时去焦点或者按下回车的时候把文本框输入的值给原先的元素即可。
JS AD 02
构造函数与原型
ES6之前没有类和继承的概念。
基于构建函数和原型来实现这些功能的。
ES6之前,对象不是基于类创建的,是通过构造函数这一特殊函数来定义对象和特征的。
创建对象示例:
// 1. 利用 new Object() 创建对象
var obj1 = new Object();
var obj = new Object();
obj.uname = 'nancy';
obj.sex = female;
obj.sayHi = function() {
console.log('Hi~');
}
// 2. 利用 对象字面量创建对象
var obj2 = {};
var obj2 = {
username: "charlotte",
age:18,
sex:female,
sayHi: function() {
console.log('hi~');
}
}
// 3. 利用构造函数创建对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function() {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh);
ldh.sing();
zxy.sing();
class方法示例:
class Star {
constructor(uname,age) {
this.uname = uname;
this.age = age;
}
say() {
console.log('hi');
}
}
var ldh = new Star('刘德华',18);
console.log(ldh.uname);
js是一门动态的语言
构造函数法注意事项:
1.实例成员就是构造函数内部通过this指定的成员,比如name,age等。
实例成员需要通过实例化之后的对象来进行访问,实例化对象指的是,var ldh = new Star(‘刘德华’,18);中的ldh。
2.静态成员,在构造函数本身上添加的成员,sex就是静态成员.静态成员 – 直接给构造函数添加的成员称为静态成员
Star.sex = ‘男’;
静态成员只能通过构造函数访问,不能通过对象访问。通过console.log(ldh.sex)会报错。
function Hero(name, blood, weapon) {
// 实例成员 / 对象成员 -- 跟对象相关的成员,将来使用对象的方式来调用
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = function () {
console.log(this.weapon + ' 攻击敌人');
}
}
// 静态成员 -- 直接给构造函数添加的成员
// Warning 这里是给了构造函数本身
Hero.version = '1.0';
var hero = new Hero('刘备', 100, '剑');
hero.attack();
var hero1 = new Hero('关羽', 100, '刀');
hero1.attack();
// 静态成员不能使用对象的方式来调用
console.log(hero.version); 注意这里的h是小写
// 静态成员使用构造函数来调用
console.log(Hero.version); //注意这里的H是大写
构造函数问题:内存浪费。简单数据类型直接复制,多个同样的function复杂数据类型,需要开辟新的内存空间存放。 比较两个函数是比较的地址。
原型 prototype
构造函数通过原型分配的函数是所有对象共享的。js规定,每个构造函数都有一个prototype(原型)属性,指向另一个对象。此对象的所有属性方法会被构造函数拥有。
console.dir(Star)
可以把不变的方法直接定义在prototype对象上,所有对象都可以共享这些方法。
原型是一个对象,作用是共享方法。公共属性定义在构造函数里,公共方法定义在原型中。
对象的原型__proto__
对象都有一个属性,__proto__ ,指向构造函数的prototype原型对象。
简而言之,对象通过__proto__ 属性,指向了prototype对象。这二者是等价的。
打印对象原型console.log(obj1)
查看是否等价 console.log(oobj1.__proto__ === Star.prototype)
constructor属性
对象的原型(_proto_)和构造函数原型对象(prototype)里面都有一个属性:constructor属性。constructor我们称为构造函数,此函数指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,Star.prototype.constructor也会指向Star。
存储于prototype的对象较多时可以用对象形式来存储。但是以此形式将会覆盖prototype原有全部内容,导致原有默认属性方法丢失,constructor失效。
此时可以手动重新指向构造函数。
function Star(uname,uage) {
this.name = uname;
this.age = uage;
}
Star.version = 1.0
//对象形式来存储较多方法
Star.prototype = {
//手动指回原来构造函数
constructor:Star,
sing:function(){
console.log('i can sing')
},
movie:function(){
console.log('i can perform movie')
}
}
var ldh = new Star('ldh',18)
ldh.sing()
console.log(ldh)
console.log(ldh.__proto__)
console.log(Star.prototype)
console.log(ldh.__proto__ === Star.prototype)
构造函数实例原型之间关系
原型链
JS成员查找机制
按照原型链来查找,一级一级上推,直到null。
静态类型与动态类型
静态类型语言:
是指在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型,某些具有类型推导能力的现代语言可能能够部分减轻这个要求.。
动态类型语言:
是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。如python中,变量a=1,那a的类型就是整型,若a=”abc”,a的类型就是字符串。
构造函数this指向
谁调用指向谁
在构造函数中,this指向对象实例。原型对象中的方法的this指向实例对象。
扩展内置对象
可通过原型对象,对原来的内置对象进行扩展自定义的方法。
比如给数组对象添加求偶数和求和的操作。
注意:数组和字符串内置对象不能给原型对象覆盖。prototype = {},只能是Array.prototype.xxx = function(){}。
继承
ES6之前的继承
ES6之前没有extends继承。可以通过构造函数+原型对象模拟继承,称为组合继承。
call()
调用前面的 函数,并修改函数运行时的this指向
fun.call(thisArg,arg1,arg2...)
thisArg :当前调用函数this的指向对象
arg1,arg2 传递的其他参数
借用构造函数继承父类型属性方法
继承父类型属性
function Father(faname,faage){
this.name = faname,
this.age = faage
}
function Son(sonname,sonage,score){
Father.call(this,sonname,sonage)
this.score = score
}
var son = new Son('ldh',19,100)
console.log(son)
继承父类型方法
错误示范:son.prototype = father.prototype; 这样直接赋值会导致共用一个内存地址,修改一个动另外一个。
正确写法:son.prototype = new father();
如果利用了对象的形式修改了原型对象,需要用constructor指回原来的构造函数。
Son.prototype.constructor = Son;
ES5 新增方法
es5新增了一些方法,可以很方便地操作数组或者字符串,主要包括:
数组方法
字符串方法
对象方法
1.数组方法
forEach()
arry.forEach(function(currentValue,index,arr))
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身
filter()
function返回为true的时候,filter把此数据返回,存入新数组。
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index,array) {
//参数一是:数组元素
//参数二是:数组元素的索引
//参数三是:当前的数组
return value >= 20;
});
console.log(newArr);//[66,88] //返回值是一个新数组
some()
some 查找数组中是否有满足条件的元素 返回值是布尔值,只要查找到满足条件的一个元素就return true 立马终止循环
var arr = [10, 30, 4];
var flag = arr.some(function(value,index,array) {
//参数一是:数组元素
//参数二是:数组元素的索引
//参数三是:当前的数组
return value < 3;
});
console.log(flag);//false
filter也是查找,返回的是数组,把所有满足条件的项目都返回,some查找的是一个布尔值,找到一个就终止,效率高。
map()
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
将数组的每个元素传递给指定的函数处理,并返回处理后的数组,所以 [‘1’,‘2’,‘3’].map(parseInt) 就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。即分别求出 parseInt(‘1’,0), parseInt(‘2’,1), parseInt(‘3’,2)的结果。
var numbers = [4, 9, 16, 25];
function myFunction() {
x = document.getElementById("demo")
x.innerHTML = numbers.map(Math.sqrt);
}
//结果 2345
every()
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
every() 方法使用指定函数检测数组中的所有元素:
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。
var ages = [32, 33, 16, 40];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.every(checkAdult);
}
//结果:false
使用这些最后需要用一个数组来接收新数据。
数组方法:every()和some()用法区别
1.every()意思
总结:一假即假 ,而且只要有一个元素是假,其后面的元素将不再遍历。
2.some()用法
总结:一真即真
3.二者应用场景
var computers = [
{ name: "mac", ram: 32 },
{ name: "mac", ram: 8 },
{ name: "IBM", ram: 16 },
{ name: "IBM", ram: 64 }
];
var everyComputerCan;
var someComputerCan;
//判断每一个元素的ram是否都大于16
var everyBoolan = computers.every(function(item) {
return item.ram > 16;
});
//判断元素的ram是否都大于16
var someBoolean = computers.some(function(item) {
return item.ram > 16;
});
console.log(everyBoolan); //结果:false
console.log(someBoolean);//结果: true
2.字符串方法
trim() 删除字符串首尾的空格。 value.trim();
3.对象方法
1 .Object.keys(obj1)
用于获取对象自身所有的属性(属性名字)。
效果类似for …in ,
返回一个由属性名组成的数组。var arr = Object.keys(obj1)
2 .Object.defineProperty()
定义对象中的新属性或者修改原有属性,
Object.defineProperty(obj,prop,descriptor);
obj:目标对象,必需
prop:被定义或被修改的属性,如果没有则添加,必需
descriptor:目标属性所拥有的特性,必需
Object.defineProperty(对象,修改或新增的属性名,{
value:修改或新增的属性的值,
writable:true/false,//如果值为false 不允许修改这个属性值
enumerable: false,//enumerable 如果值为false 则不允许遍历
configurable: false //configurable 如果为false 则不允许删除这个属性 或 是否可以再次修改特性(此处的特性指的是这里大括号里面写的这些特性,不是属性本身)
})
其他:
delete obj1.a 可以删除obj1当作的a属性和这个属性的值。
案例 查询商品价格
// 利用新增数组方法操作数据
var data = [{
id: 1,
pname: '小米',
price: 3999
}, {
id: 2,
pname: 'oppo',
price: 999
}, {
id: 3,
pname: '荣耀',
price: 1299
}, {
id: 4,
pname: '华为',
price: 1999
}, ];
// 1. 获取相应的元素
var tbody = document.querySelector('tbody');
var search_price = document.querySelector('.search-price');
var start = document.querySelector('.start');
var end = document.querySelector('.end');
var product = document.querySelector('.product');
var search_pro = document.querySelector('.search-pro');
setDate(data);
// 2. 把数据渲染到页面中
function setDate(mydata) {
// 先清空原来tbody 里面的数据
tbody.innerHTML = '';
mydata.forEach(function(value) {
// console.log(value);
var tr = document.createElement('tr');
tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>';
tbody.appendChild(tr);
});
}
// 3. 根据价格查询商品
// 当我们点击了按钮,就可以根据我们的商品价格去筛选数组里面的对象
search_price.addEventListener('click', function() {
// alert(11);
var newDate = data.filter(function(value) {
return value.price >= start.value && value.price <= end.value;
});
console.log(newDate);
// 把筛选完之后的对象渲染到页面中
setDate(newDate);
});
// 4. 根据商品名称查找商品
// 如果查询数组中唯一的元素, 用some方法更合适,因为它找到这个元素,就不在进行循环,效率更高]
search_pro.addEventListener('click', function() {
var arr = [];
data.some(function(value) {
if (value.pname === product.value) {
// console.log(value);
arr.push(value);
return true; // return 后面必须写true
}
});
// 把拿到的数据渲染到页面中
setDate(arr);
})
JS AD 03 函数进阶
函数定义方式
1 自定义函数(命名)
function fn() {};
2 函数表达式(匿名函数)
var fun = function() {};
3 利用newFunction(‘参数1’,‘参数2’,‘函数体’);
var f = new Function();
var fn = new Function('a','b','console.log(a + b)');
fn(1,2);
instanceof 函数 检测前者是不是属于后者。输出一个布尔值。
console.log(fn instanceof Object );
//检测fn 是不是对象
//结果:true
js中万物皆对象
构造函数f对象实例和Function原型对象之间的关系
函数调用
目前已经学习6种函数
1.普通函数 fn() ; fun.call()
2.对象的方法 obj1.sayHi()
3.构造函数 new Star()
4.绑定事件函数 btn.onclick=function(){}
匿名函数
5.定时器函数 setInterval(function(),1000)
6.立即执行函数 (function(){}()) , (function(){})()
this指向问题
1 普通函数指向window,
2 对象的方法指向对象,
3 构造函数指向实例化对象,
4 绑定事件函数指向绑定事件对象,
5 定时器 完整写法window.setTimeout
指向window,
6 立即执行函数 指向window
js提供了 一些函数方法来处理this指向问题,如 call(),apply(),bind()
1 .call()
fun.call(thisArg,arg1,arg2...)
作用1 调用函数 2 改变this指向
主要作用可以实现继承
2 .apply()
fun.apply(thisArg,[argsArry])
主要是调用一个函数,和call的区别在于参数要是一个数组。
var arr1 = [1,2,3]
var max = Math.max.apply(null,arr1)
//此处的指向可以指向null 最好指向Math本身
3 .bind()
不调用函数,只改变函数内部this指向
fun.bind(thisArg,arg1,arg2)
返回由指定的this值和初始化参数改造的原函数拷贝,即返回了一个改造后的函数
var btn1 = document.querySelector('#btn1')
btn1.addEventListener('click',function(){
this.disabled = true
setTimeout(function(){
this.disabled = false
}.bind(this),3000)
})
严格模式
安全 避免歧义,预留ES6的关键字,strict
1 .脚本严格模式
在脚本之前添加特定语句’use strcit’;
<script>
'use strict'; //当前script标签开启了严格模式
</script>
//或者
<script>
(function(){ //在当前的这个自调用函数中有开启严格模式,当前函数之外还是普通模式
'use strict';
})();
</script>
2 函数严格模式
仅在此函数内开启,其他函数不受影响
function fn() {
'use strict';
}
注意:
1 严格模式下,不能不声明就使用一个变量。
2 删除变量 用 delete ,严格模式下不能随意删除已经声明的变量。
3 严格模式下this指向问题:全局作用域中的函数,this指向undefine
4 构造函数不加new调用,this会报错
5 严格模式下,定时器 this 还是指向 window
严格模式下的函数
1 不能有重名参数
2 函数必须声明在顶层 不能在if for里面再写函数
高阶函数
1 接收函数作为参数 2 把函数作为返回值输出
满足任一条件即为高阶函数
回调函数是在前面所有参数和代码执行完毕之后再执行。
闭包
闭包指的是有权访问另一函数作用域中变量的函数。
chrome中查看闭包的存在:
F12->Sources->js文件 ->右侧调试区Scope->Closure
函数也是一种参数类型,所以可作为一个参数被返回。
闭包的作用,延伸了变量的作用范围。
for循环是一个同步任务,进入之后就会直接执行小括号内的代码,但是在大括号内的内容可能是异步的,比如一个onclick触发事件。
闭包案例
<ul>
<li>猪肉芹菜饺子</li>
<li>鲅鱼白菜饺子</li>
<li>牛肉番茄饺子</li>
<li>羊肉胡萝卜饺子</li>
</ul>
//错误示例:
var lis = document.querySelectorAll('li')
for(var i = 0;i<lis.length;i++){
setTimeout(function(){
console.log(lis[i].innerText)
},3000)
}
//将会报错,主要是因为setTimeout是异步任务,for中的i是同步,直接执行导致最后,i=4,然后才去执行setTimeout中的函数,lis没有4,所以报错。
//正确示例
var lis = document.querySelectorAll('li')
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerText)
}, 3000)
})(i)
}
递归
可以在函数内部调用自身,此函数为递归函数
死递归 导致 栈溢出
使用return返回,并设定退出条件
var num = 1;
function(){
console.log('我要打印6句话');
if(num == 6)
{
return;
}
num++;
fn();
}
fn();
递归求1-n的阶乘
function fun(n) {
if (n == 1) {
return 1
}
return n * fun(n - 1)
}
var num = fun(3)
console.log(num)
递归求二层数据
var data = [{
id: 1,
name: 'jiadian',
goods: [{
id: 11,
name: 'bingxiang'
},
{
id: 22,
name: 'kongtiao'
}]
},
{
id: 2,
name: 'fushi'
}
]
function getID(json, id) {
var obj1 = {}
json.forEach(function (item) {
if (item.id == id) {
obj1 = item
}
else if (item.goods && item.goods.length > 0) {
obj1 = getID(item.goods, id)
}
}
)
return obj1
}
console.log(getID(data, 11))
item()
一直不知道javascript还有类似jQ里面eq()的函数,原来原生javascript的item()有类似功能:
下面来说明下item()的用法:
定义和用法
item() 方法节点列表中位于指定索引的节点。
以下两条语法产生相同的结果:
document.body.childNodes.item(0);
document.body.childNodes[0];
深拷贝与浅拷贝
浅拷贝只拷贝一层,更深层次的对象级别的拷贝只拷贝引用。如果在副本中修改,直接通过地址指向内存中的数据,修改后原来的对象数据也被修改。
深拷贝拷贝多层,每一级别的数据都会拷贝
Object.assign(targetobj,originalobj);浅拷贝语法糖 ES6
浅拷贝:
//1 用for循环来浅拷贝
for( var k in obj){
copy1[k] = oriObj[k]
}
//2 assign浅拷贝
Object.assign(copy1,oriObj)
深拷贝:
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item)
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o, obj);
console.log(o);
var arr = [];
console.log(arr instanceof Object);
o.msg.age = 20;
console.log(obj);
JS AD 04
正则表达式
正则,regular expression ,用于匹配字符串中字符的组合模式。也是对象。可以用于验证表单等。
创建两种办法
1 利用 RegExp 对象的构造函数创建
var 变量名 = new RegExp(/ 表达式/); var regExp=new RegExp(/123/)
2 字面量创建
var 变量名 = //; var rg = /123/;
检测办法test()
test() ,返回 true 或者 false。符合规则的,返回true,反之返回false。
regexObj.test(str);
1 . regexObj 是写的正则表达式规则
2 . str是被检测文本
console.log(re.test(123));
语法规则
边界符
^ 开始符,开始的时候需要以这个为开头
$ 结尾符,结束的时候需要以这个为结尾
字符类
[] 字符集,表示有一系列字符可供选择,只要匹配其中一个就行
var rg1 = /^[a-z]$/;
var rg1 = /^[a-zA-Z0-9-_]{3,16}$/;
//如果中括号里面有^,则表示取反的意思,不能包含后面全部的内容
console.log(rg1.test('a'));
// console.log(rg1.test('b'));
// console.log(rg1.test('c'));
// console.log(rg1.test('abc'));
量词符
* 量词符号 ,规定前面的字符可以出现 0 次或者很多次,>=0。var reg1 = //
+相当于出现1 或者很多次,>=1。
?相当于1 || 0
{n} 量词符,前面的一个字符重复n次,{n,}重复n次以上,{n,m}重复n-m次
() 表示优先级
预定义类
正则中的 ‘或’ 符号是用一个’|’ 来表示的。
检测中文昵称:/^[\u4e00-\u9fa5]{2,8}$/
三种检测方法
replace()
replace() [参数 /修饰符 ]
g全局匹配 i 忽略大小写
var str = document.getElementById("demo").innerHTML;
var txt = str.replace(/microsoft/i,"Runoob");
test()
var patt = /e/;
patt.test("The best things in life are free!");
exec()
exec() 方法是一个正则表达式方法,用于检索字符串中的正则表达式的匹配。
该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
以下实例用于搜索字符串中的字母 “e”:
/e/.exec("The best things in life are free!");
JS AD 05 ES6
js语言的标准规范
一 ES6的新增语法
let 关键字
有了块级作用域
应用场景:1 防止循环变量变成全局变量;2 不存在变量提升;3 具有暂时性死区(即被绑定在当前作用域,外部不会影响到不可使用)
let arr1 = [];
for (let i = 0;i <2;i++){
// var num = i;
arr1[i] = function(){
console.log(i);
}
// arr1[i] = num;
}
arr1[0]();
arr1[1]();
// console.log(arr1[1]);
// console.log(arr1[0]);
面试题
未使用块级作用域:
此处arr[i]语句执行时,仅仅对数据进行赋值,赋值一个函数,但是函数没有调用。下面调用的时候,在函数体内没有i变量的定义,所以程序向上寻找,在作用域链引导下,找到上级已经执行完的变量i,此时i = 2。
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
//结果为 2 2
使用块级作用域:
调用时,向上级寻找i,因为i有块级作用域,每个作用域对应一个函数,所以,各是各的。
let arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
const 常量
对应的内存地址不可更改,也就是值不可更改。
特点:1 具有块级作用域 2 内存地址不可以更改 3 声明的时候就要赋值。
简单数据类型,不能修改数据。
复杂数据类型,不可更改内存地址。可以修改内部的数据,但是不能给整个复杂数据类型直接重新赋值。因为重新赋值需要重新安排内存地址,所以不行。
const ary = [11, 22];
console.log(ary[1]);
ary[1] = 100;
console.log(ary[1]);//常量内部值可以更=更改
ary = [100,200];// 报错 不允许常量整体重新赋值,需要重新分配内存地址。
解构赋值
解构:分解数据结构。
ES6 允许从数组中提取值,按照对应位置对变量赋值。对象也可以实现解构。
数组解构
一一对应,如果接收数量不足,则原数组尾部数据舍弃。如果接收数量大于原数组数量,接收数不足的部分为undefined
let ary1 = [1, 2, 3]
let [a, b] = ary1
console.log(a)
console.log(b)
let ary1 = [1, 2, 3]
let [a, b, c, d] = ary1
console.log(a)
console.log(b)
console.log(c)
console.log(d)
对象解构
两种写法
//这种写法需要和原对象中的属性名一一对应,不能修改
let person = { name: 'zhangsan' }
let {name} = person
console.log(name)
//重新命名的写法
let person = { name: 'zhangsan' }
let {name:copyName} = person
console.log(copyName)
箭头函数
形式:()=>{}
作用简化函数定义语法
const fn = ()=>{}
如果函数体中只有一句代码。且代码的执行结果就是返回值,可以省略大括号。
如果参数只有一个可以省略括号。
const fn = v => alert(v)
fn(20)
不定义this ,指向的是函数定义位置上下文的this。继承父级的this
对象不能产生作用域 ,内部方法定义在全局作用域下。
面试题一例:
var obj = {
name:'zhangsan',
age:'200',
say:()=>{
alert(this.age)
}
}
obj.say()
运行结果 alert了一个undefined 。原因:obj是对象没有作用域,所以实际运行在window对象下。
验证代码:
var age=200
var obj = {
name:'zhangsan',
say:()=>{
alert(this.age)
}
}
obj.say()
剩余参数
实参数量大于形参,多出来的叫剩余参数。
剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
三个连续的点具有两个含义:展开运算符(spread operator)和剩余运算符(rest operator)。
… :展开运算符,将一个数组转为用逗号分隔的参数序列
例:
const sum = (...args) => {
let total = 0;
args.forEach(item => total += item);
return total;
};
console.log(sum(10, 20))
console.log(sum(10,20,30))
函数 sum里面,形式参数args的前面添加了三个…,代表可以接受剩余参数,在前面的形式参数与实参一一对应后,接受剩余的全部实参。
例:
let ary1 = ['zhangsan', 'lisi', 'wangwu'];
for (var k in ary1) {
console.log(ary1[k]);
//obj[k] 得到 属性里面的值。
}
let [s1,...s2] = ary1;
console.log(s1);
console.log(s2);
这个例子中,同时使用了剩余参数和结构赋值。
二 ES6的内置对象扩展
ARRY的扩展方法
1 扩展运算符
剩余参数 是把剩余的参数放在一个形参中
扩展运算符 是把数组或者对象转为用逗号分隔的参数序列。
作用1 合并数组
//案例1
arry3 = [...arry1,...arry2];
//案例2
ary1.push(...ary2)
作用2 将类数组转换为真的数组
let oDivs = document.getElementsByTagName('div');
console.log(oDivs);
var oDivs1 = [...oDivs];
console.log(oDivs1);
2 Arry.from()
这是一个构造函数方法。
将类数组或者可以遍历的对象转换为真正的数组
let arryLike = {
"0":1,
"1":2,
"2":3,
"length":3
}
for (var k in arryLike){
console.log(arryLike[k]);
}
var ary111 = Array.from(arryLike);
console.log(ary111);
for (var k in ary1){
console.log(ary111[k]);
}
Arry.from() 还可以接收第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,处理后的数组放入返回的数组。
var ary2 = Arry1.from(arryLike,item=>item*2)
//结果为[0,2,4]
3 find()
用于找出数组中第一个符合条件的值,没找到返回undefined
let arry1 = [{ id: 1, name: 'zhangsan' }, { id: 2, name: 'lisi' }]
let target = arry1.find(item => item.id == 2)
console.log(target)
4 findindex()
查找数组第一个符合条件的位置,没有找到返回-1
5 includes()
表示某个数组中是否包含某个值。返回布尔值。 indexof 是老方法
模板字符串
ES6新增创建字符串方法使用反引号定义
let name = `这是一个模板字符串`
特点
1 可以解析变量 ${}
let name = `zhangsan`
let sayHello = `hello my name is ${name}`
console.log(sayHello)
2 内部可以换行 普通字符串不能换行显示
startsWith() 和 str.endsWith()
var str = 'hello world !'
console.log(str.startsWith('hello'))
console.log(str.endsWith('!'))
repeat()
console.log("y".repeat(5))
Set
ES6提供的新的数据结构,类似数组,内部成员都是唯一的不能有重复。
Set本身是一个构造函数,可以接受一个数组作为参数用来初始化。
const s = new Set([1,2,3,4,4])
也拥有forEach方法
Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
实例方法
- add(value):添加某个值,返回 Set 结构本身
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
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 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
s.forEach(value => console.log(value))