面向对象是把事务分解成为一个个对象,然后由对象之间分工合作。
例子:把大象装进冰箱
区别
面向对象的思维特点:
对象:
对象是一个具体的事物。在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象。
对象是由属性和方法组成的
属性:事物的特征,在对象中用属性来表示
方法:事物的行为,在对象中用方法来表示
类:在es6中增加了类的概念,可以使用class关键字声明一个类。
创建一个类和对象:
class gender{
}
new gender();
constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象时,窗体加载时自动调用该方法,如果没有显式定义,类内部会自动给我们创建一个constructor()
//创建类
class Start{
//构造函数
constructor(name){
// this就是创建的实例
this.name = name;
}
}
// 利用类创建对象
var people = new Start('卑微小陈'); //调用类时,只要加了参数,将自动调用构造函数方法
//使用
console.log(people.name); //在浏览器中打印输出 卑微小陈
注意:
//创建类
class Start{
//构造函数
constructor(name){
// this就是创建的实例
this.name = name;
}
//方法
// 类里面的所有的函数不需要写关键字function
// 多个函数方法之间不需要添加逗号分隔
taking(content){
console.log(this.name + content);
}
}
// 利用类创建对象
var people = new Start('卑微小陈说:');
people.taking('上帝撒了一把智慧,而我却打了一把伞~')
console.log(people);
子类可以继承父类的方法和属性:
// 父类
class Father{
money(){
console.log(10000000)
}
}
// 子类 继承(extend) 父类
class Son extend Father{
}
var son = new Son();
son.money(); //打印输出10000000
super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
calss 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);//调用父类中的构造函数
}
}
var son = new Son(2,4);
son.sum(); //打印输出6
注意:
字类类可以调用父类的方法:
class Father{
say(){
return '我是爸爸';
}
}
class Son extends Father{
say(){
// 打印输出 字类 的say方法
// console.log('我是儿子');
//子类调用父类里面的方法
// super.say()就是调用父类中的普通函数say这个函数
console.log(super.say() + '的儿子');
}
}
var son = new Son();
son.say();
子类继承父类加法方法 同时 扩展减法方法
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必须在子类this之前调用
super(x,y);
this.x = x;
this.y = y;
}
substract(){
console.log(this.x - this.y);
}
}
var son = new Son(5,2);
//调用子类中的substract方法
son.substract(); //打印输出3
//调用子类从父类中继承的sum方法
son.sum(); //打印输出7
<body>
<button>点我</button>
<script>
var that;
var _that;
class Strat{
// construcror 里面的this指向的是创建的实例对象
constructor(name,age){
that = this;
console.log(that); //打印输出实例对象 ‘华晨宇’
this.name = name;
this.age = age;
//调用Sing方法
//this.sing();
//通过按钮点击事件来调用sing方法
this.btn = document.queryString('button'); //通过标签选择器选中按钮
this.btn.onclick = sing; //注意sing方法后面不要加小括号哟,加小括号后立马调用sing方法
}
sing(){
//sing方法里面的this指向的是btn这个按钮,因为btn调用了sing这个方法
//console.log(this.name); //输出undefined
console.log(that.name); //输出华晨宇
}
dance(){
//这个dance里面的this指向的是实例对象 people,因为people调用了这个函数
_that = this;
console.log(_that); //打印输出华晨宇
}
}
// 实例化Strat方法
var people = new Strat('华晨宇');
people.dance();
</script>
</body>
在典型的OOP(面向对象)的语言中(如java),都存在类的概念,类就是对象的模板,对象就是类的实例,但是在ES6之前,js并没有引入类的概念。
在ES6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和他们的特征。
创建对象的方式:
// 对象字面量
var obj1 = {};
// new Object()
var onj2 = new Object();
//自定义构造函数
function Start(name,age){
this.name = name;
this.age = age;
}
//通过构造函数生成不同的对象
var sin = new Start('易烊千玺',20);
构造函数是一种特殊的函数,主要用来出啊实话对象,即对象成员变量赋初始值,它总是与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在js中,使用构造函数时要注意以下两点:
new在执行时会做的四件事情:
构造函数中的属性和方法我们成为成员,成员可以添加
成员可以分为:实例成员和静态成员
实例成员
静态成员
构造函数的方法很好用,但是存在浪费内存的问题。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qymgR9lF-1585145600549)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200321115420071.png)]
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript规定,每一个构造函数都有一个prototype属性,指向另外一个对象,注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上面,这样所有的对象都可以共享这些方法。
__proto__
对象都会有一个__proto__
指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型存在。
__proto__
对象原型和原型对象prototype
是等价的对象原型(proto)和构造函数(prototype)原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它只会构造函数本身。
constructor主要用于记录对象引用于那个构造函数,它可以让原型对象重新指向原来的构造函数。
function Start(name,age){
this.name = name;
this.age = age;
}
//Start.prototype.sing = function(){
// console.log('唱歌');
//}
//很多情况下,我们需要手动的利用constructor这个属性 指回 原来的构造函数
Start.prototype = {
//让构造函数指回原来的构造函数
//如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor:Start,
sing:function(){
}
movie:function(){
}
}
var l = new Start('卑微小陈',20);
__proto__
)。__proto_
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说提供一条路线。可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组添加自定义求偶书和的功能。
数组的原型对象:
注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xx = function(){}的方式
console.log(Array.prototype);
// 原型对象的应用 扩展内置对象方法
// 在原来的方法里面追加一个方法
Array.prototype.sum = function() {
var sum = 0;
for(var i = 0; i < this.length; i++){
sum += this[i];
}
return sum;
}
// 错误
// 数组和字符串内置对象不能给原型对象覆盖操作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, 3];
console.log(arr.sum());
console.log(Array.prototype);
Es6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
调用这个函数,并且修改函数运行时的this指向。
fun.call(thisArg,arg1,arg2,...)
function like(x, y) {
console.log('想要自由');
console.log(this); //当前this指向window
console.log(x + y);
}
var o = {
name: 'Harty'
};
//调用函数
// like();
// 1 call()可以调用函数
like.call();
// 2 call()可以修改like方法里面的this指向
like.call(o,1,2); // 此时函数的对象就指向了o这个对象 打印输出3
核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。
// 1 父构造函数
function Father(name,age){
// this指向父构造函数的对象实例
this.name = name;
this.age = age;
}
// 2 子构造函数
function Son(name, age, score){
// this指向子构造函数的对象实例
Father.call(this,name,age); //调用父构造函数,将父构造函数的this指向变成子构造函数的this指向
// 添加子构造函数自身的数据
this.score = score; //score既继承了父构造函数的数据,也有自身的数据
}
var son = new Son('卑微小陈', 20, 59);
console.log(son);
// 1 父构造函数
function Father(name,age){
// this指向父构造函数的对象实例
this.name = name;
this.age = age;
}
// 给父构造函数添加方法
Father.prototype.money = function(){
console.log(100000);
}
// 2 子构造函数
function Son(name, age, score){
// this指向子构造函数的对象实例
Father.call(this,name,age); //调用父构造函数,将父构造函数的this指向变成子构造函数的this指向
// 添加子构造函数自身的数据
this.score = score; //score既继承了父构造函数的数据,也有自身的数据
}
// 使用添加的父构造函数的方法
// 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起修改
// Son.prototype = Father.prototype;
Son.prototype = new Father(); //实例父原型对象,然后给子原型对象
//如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son;
// 给子构造函数添加方法(子构造函数专门的方法)
Son.prototype.worker = function(){
console.log('程序媛');
}
var son = new Son('卑微小陈', 20, 59);
console.log(son);
类的本质还是一个函数,我们也可以简单的认为类就是构造函数的另外一种写法。
构造函数的特点:
构造函数有原型对象prototype
构造函数原型对象prototype里面有constructor指向构造函数本身
构造函数可以通过原型对象添加方法
构造函数创建的实例对象有__proto__
原型指向构造函数的原型对象
class Start{
}
console.log(typeof Start); //function
// 1)构造函数有原型对象prototype
console.log(Start.prototype);
// 2)构造函数原型对象prototype里面有constructor指向构造函数本身
console.log(Start.prototype.constructor);
// 3)构造函数可以通过原型对象添加方法
Start.prototype.sing = function(){
console.log('唱歌');
}
// 4)构造函数创建的实例对象有`__proto__`原型指向构造函数的原型对象
var lyf = new Start();
console.dir(lyf);
console.log(lyf.__proto__ === Start.prototype);
类的所有方法都定义在类的prototype属性上
所以es6的类它的绝大部分功能,es5都能做到,新的class写法只是让对象原型的写法更加清晰,更像面型对象的语言
所以es6的类就是语法糖
语法糖:语法糖就是一种便捷的写法,简单的理解,有两种方法可以实现同样的功能,但是一种写法更加清晰,方便,那么这个方法就是语法糖。
数组方法
迭代(遍历)方法:forEach(),map(),filter(),some(),every()
forEach()
array.forEach(function(currentValue,index,arr));
currentVaule:数组当前项的值
index:数组当前项的索引
arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBpYnt9i-1585145600550)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322135502793.png)]
filter()—过滤器
array.filter(function(currentValue,index,arr))
filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组。
注意它直接返回一个新数组。
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpNS9dCT-1585145600551)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322140323797.png)]
some()
array.some(function(currentValue,index,arr))
some()方法用于检测数组中的元素是否满足指定条件,通俗的讲就是数组中是否有满足条件的元素
注意它的返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到,就返回false
如果找到第一个满足条件的元素,则终止循环,不再继续查找
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqfWggoF-1585145600552)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322141030520.png)]
字符串方法
trim()方法
trim()方法会从一个字符喜欢的两端删除空白字符
str.trim()
trim()方法本身并不影响原字符串本身,它返回的是一个新的字符串
var str = ' 卑微小陈 在线找工作 ';
console.log(str);
console.log(str.trim());
对象方法
Object.defineProperty()定义对象中新属性或修改原有的属性
Object.defineProperty(obj,prop,descriptor)
obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性,以对象{}的形式书写
value:设置属性的值。默认undefined
writeable:值是否可以重写。true|false,默认为false
enumerable:目标属性是否可以被枚举。true|false,默认false
configurable:目标属性是否可以被删除或是否可以再次修改特性true|false,默认false
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<script type="text/javascript">
var obj = {
id: 1,
name: '小陈',
hobby: '代码编程'
};
// 1 以前的调价和修改属性的方式
// obj.age = 20;
// console.log(obj);
// 2 Object.defineProperty()定义新属性或修改原有的属性
Object.defineProperty(obj, 'age', {
// 如果里面有age属性就修改操作,没有就做添加操作
value: 20 //给obj数组里面,添加一个age属性,并且age属性的属性值为20
});
console.log(obj);
Object.defineProperty(obj, 'id', {
writable: false,//值为false,不允许修改这个属性值,默认为false
});
obj.id = 2;
console.log(obj);
Object.defineProperty(obj, 'address', {
value: '湖南长沙',
// enumberable如果为false,则不允许遍历
enumberable: false,
// configurable如果为false,则不允许被删除和再次修改这个特性,默认为false
configurable: false
});
console.log(obj);
console.log(Object.keys(obj));
delete obj.address; //删除address信息
console.log(obj);
delete obj.hobby; //删除hobby信息
console.log(obj);
</script>
</body>
</html>
根据数组方法来查询数据:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
.content {
width: 800px;
height: auto;
margin: 50px auto;
}
.search input{
margin-left: 10px;
}
input[type = 'text']{
width: 50px;
}
table{
margin-top:30px;
}
tbody{
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div class="search">
按照条件查询:<input type="text" class="start"/> - <input type="text" class="end"/> <input type="button" class="search_price" value="搜索" />
按照商品名称查询:<input type="text" class="select_name"/><input type="button" class="select_btn" value="查询" />
</div>
<table border="1px" cellspacing="0" cellpadding="0" width="500px">
<thead>
<tr>
<th>id</th>
<th>产品名称</th>
<th>价格</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script type="text/javascript">
var data = [{
id: 1,
name: '华为',
pice: 10000
},{
id: 2,
name: 'opp',
pice: 7000
},
{
id: 3,
name: 'vivo',
pice: 6666
},
{
id: 4,
name: 'iPhone',
pice: 15000
},
{
id: 5,
name: '小米',
pice: 7000
},
{
id: 6,
name: '三星',
pice: 6500
}];
// 获取相应的元素
var tbody = document.querySelector('tbody');
// 根据价格查询按钮
var search_price = document.querySelector('.search_price');
// 起始价
var start = document.querySelector('.start');
// 结束价格
var end = document.querySelector('.end');
// 商品名称
var select_name = document.querySelector('.select_name');
var select_btn = document.querySelector('.select_btn');
// 页面加载将所有的数据渲染到页面上面
setData(data);
// 渲染页面的方法
function setData(mydata){
// 先清空原来tbody的数据
tbody.innerHTML = '';
// 把数据渲染到页面中
mydata.forEach(function(value){
// console.log(value); value的值是一个Object
// 创建一个行
var tr = document.createElement('tr');
// 创建单元格
tr.innerHTML = '' + value.id + ' ' + value.name + ' ' + value.pice + ' ';
// 将创建的单元格放到tbody中
tbody.appendChild(tr);
});
}
// 根据价格查询商品
// 当我们点击了按钮,就可以更具我们的商品价格去筛选数组里面的对象
search_price.addEventListener('click',function(){
// alert(11)
var newData = data.filter(function(value){
// value指的是每个数组元素
return value.pice >= start.value && value.pice <= end.value;
})
// console.log(newData);
// 将筛选完成之后的数据渲染到页面上面
setData(newData);
});
// 根据商品名称来查找数据
select_btn.addEventListener('click',function(){
// 因为拿到的数据是一个对象,所以我们要声明一个数组来接收
var arr = [];
// 如果查询数组中唯一的元素,用some方法,因为它找到这个元素,就不再进行循环,效率更高
data.some(function(value){
if(value.name === select_name.value){
// console.log(value);
// 找到对象之后,就将对象添加到数组里面去
arr.push(value);
// 如果返回的是true,则证明找到了这个对象
return true; // 注意return后面必须写 true,some方法返回的是一个布尔类型
}
});
// 把拿到的数据渲染到页面上面
setData(arr);
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<script type="text/javascript">
var arr = ['red','yellow','blue','pink'];
<!-- 1.forEach迭代遍历 -->
// forEach去遍历所有的元素
arr.forEach(function(value){
if(value === 'pink'){
console.log('找到了该元素');
return true; //forEach里面的return不会终止迭代
}
console.log('啦啦啦');
});
// 2.some遍历
// 如果查询数组中唯一的元素,用some方法更合适
arr.some(function(value){
if(value === 'yellow'){
console.log('找到了该元素yellow');
return true; //在some里面遇到return true就是终止遍历,迭代效率更高
}
console.log(11);
});
// 3.filter遍历
arr.filter(function(value){
if(value === 'blue'){
console.log('找到了该元素blue');
return true; //filter里面的return不会终止迭代
}
console.log(11);
});
</script>
</body>
</html>
函数声明的方式function关键字(命名函数)
function fn(){};
函数表达式(匿名函数)
var fn = function(){};
利用new Function(‘参数1’,‘参数2’,‘函数体’)
var f = new Function('a','b','console.log(a + b)');
fn(1,2);
注意:
Function里面参数都必须是字符串格式
这种方式执行效率低,也不方便书写,因此比较少使用
所有的函数都是Function的实例(对象)
函数也属于对象
// 1 自定义函数(命名函数)
function fnn() {};
// 2 函数表达式(匿名函数)
var fnn = function(){};
// 3 利用new Function('参数1','参数2','函数体')
var fn = new Function('a','b','console.log(a + b)');
fn(1,2);
// 4 所有的函数都是Function的实例对象
console.dir(fn);
console.log(fn instanceof Object); //判断fn函数是否是Object类型,返回的是布尔类型的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvU8E8Bk-1585145600553)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200323133814635.png)]
// 函数的调用方式
// 1 普通函数
function fn(){
console.log('卑微小陈在线学习');
}
fn(); //或者fn.call();
// 2 对象的方法
var o = {
sayHi: function(){
console.log('加油~');
}
}
o.sayHi();
// 3 构造函数
function dream(){};
new dream();
// 4 绑定事件函数
btn.onclick = function(){}; //点击按钮就可以调用这个函数
// 5 定时器函数
setInterval(function(){},1000); //这个函数是定时器自动1秒调用一次
// 6 立即执行函数
(function(){
console.log('学习');
})(); // 立即执行函数是自动调用函数
这些this的指向,是当我们调用函数的时候确定的。调用的方式的不同决定了this的指向不同
一般指向我们的调用者
调用方式 | this指向 |
---|---|
普通函数的调用 | window |
构造函数的调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法的调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
JavaScript为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部的this的只想问题,常用的有bind(),call(),apply()方法。
call方法
call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this的指向。
fun.call(thisArg,arg1,arg2,...)
var o = { name: 'Heart' };
function fn(){
console.log(this)
}
fn.call(o); //将fn函数this指向o
call的主要作用可以实现继承
function Father(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Son(name,age,sex){
// 调用Father函数,然后将this指向为Son函数,并使用Father里面的内容
Father.call(this,name,age,sex);
}
var son = new Son('千玺',19,'男');
console.log(son);
apply方法
apply()方法调用一个函数,简单理解为调用函数的方法,但是它可以改变函数的this指向。
fun.apply(thisArg,[argsArray])
thisArg:在fn函数运行时指定的this值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
var o = { name: 'Heart' };
function fn(arr){
// 也是调用函数 第二个可以改变函数内部的this指向
console.log(this);
// 但是她的参数必须是数组(伪数组)
console.log(arr); //打印出来的不是数组,是一个字符串
}
fn.apply(o,['smHeart']);
// apply 的主要应用 比如说我们可以利用apply借助数学内置对象求最大值
var arr = [1,16,9,4,8];
// 存储的就是数组中的最大值
//var max = Math.max.apply(null,arr);
var max = Math.max.apply(Math,arr);
// 打印输出数组中的最大值
console.log(max);
bind()方法不会调用函数,但能改变函数内部的this指向
fun.bind(thisArg,arg1,arg2...)
thisArg:在fn函数运行时指定的this值
arg1,arg2:传递其他参数
返回由指定的this值和初始化参数改造的原函数拷贝
var o = { name: 'Heart' };
function fn(arr){
console.log(this);
};
var f = fn.bind(o);
f(); //调用新函数f()
// 1 不会调用原来的函数 可以改变原来的函数内部的this指向
// 2 返回的是原函数改变this之后产生的新函数
// 3 如果有的函数不需要立即调用,但是又不想改变这个函数内部的this指向此时用bind
// 当我们有一个按钮,我们点击之后就禁用这个按钮,3秒钟之后开启这个按钮
<button type="button" class="btn">点我</button>
<script type="text/javascript">
var btn = document.querySelector('.btn');
btn.onclick = function(){
this.disabled = true; //这个this指向的是btn这个按钮
// var that = this;
setTimeout(function(){
// 1 给this赋值在使用改变this指向
// that.disabled = false; // 定时器里面的函数this指向的是window
// 2 bind
this.disabled = false;//这个this也是指向的btn对象
}.bind(this),3000) //这个this也是指向的btn对象
}
</script>
call,apply,bind总结
JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。Es5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行js代码。
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器会被忽略。
严格模式对正常的JavaScript语义做了一些更改:
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为脚本开启严格模式和**为函数开启严格模式****两种情况。
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’。
<script>
'use strict';
// 下面的js代码就会按照严格模式执行代码
</script>
<script>
(function(){
'use strict';
})();
</script>
<script>
function fn(){
// 给fn函数开启严格模式
'use strict';
// fn函数内部的下面的代码按照严格模式执行
}
</script>
严格模式对javascript的语法和行为,都做了一些改变。
变量规定
在严格模式下,如果一个变量没有声明就赋值,默认是全局变量,严格模式禁止这种用法,变量都必须先用var声明,然后再使用。
严禁删除已声明的变量。例如,delete x;语法是错误的。
this的指向问题
以前在全局作用域函数中this指向window对象
严格模式下全局作用域中函数的this是undefined
以前构造函数时不加new也是可以调用函数,当普通函数,this指向全局对象
严格模式下,如果构造函数不加new调用,this会报错
new实例化的构造函数指向创建的对象实例
定时器this指向的还是window对象
事件,对象还是指向调用者
函数变化
高阶函数是对其他函数进行操作的函数,它 接收函数作为参数或 将函数作为返回值输出。
把一个函数作为参数进行传递:
<script>
function fn(callback){
callback&& callback();
}
fn(function(){alert('hi')})
</script>
把一个函数作为返回值传递:
<script>
function fn(){
return function(){}
}
fn();
</script>
此时fn就是一个高阶函数
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
//高阶函数 - 函数可以作为参数传递
function fn(a,b,callback){
console.log(a + b);
callback&& callback();
}
fn(1,2,function(){
console.log('我是最后调用的');
});
变量根据作用域的不同分为两种:全局变量和局部变量。
闭包(closure)指有权访问另一个函数作用域中变量的函数。 —JavaScript高级程序设计
简单的理解就是,一个作用域可以访问另外一个函数内部的局部变量。
function fn(){
var num = 10;
// 闭包:我们fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
function fun(){
//只要是函数就会创建一个函数的作用域
console.log(num);
}
fun();
}
fn();
闭包的作用:延伸了变量的作用范围
// 我们fn外面的作用域可以访问fn内部的局部变量
function fn(){
var num = 10;
//function fun(){
//只要是函数就会创建一个函数的作用域
// console.log(num);
//}
//return fun;
return function(){
console.log(num);
}
}
var f = fn();
// 类似于
//var f = function fun(){
//只要是函数就会创建一个函数的作用域
// console.log(num);
// }
// 调用f
f();
闭包应用—点击li输出当前li的索引号
利用动态添加属性的方式
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
<li>蛇果</li>
</ul>
<script type="text/javascript">
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 动态的给li添加索引
lis[i].index = i;
lis[i].onclick = function() {
// 拿到当前索引
console.log(this.index);
}
}
</script>
利用闭包的方式得到当前小li的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也称为小闭包,因为立即执行函数里面的任意一个函数副都可以使用它的i变量
(function(i) {
lis[i].onclick = function() {
// 拿到当前索引
console.log(i);
}
})(i);
}
循环中的setTimeout()–3秒钟hi后,打印所有li元素的内容
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
<li>蛇果</li>
</ul>
<script type="text/javascript">
var lis = document.querySelector('.nav').querySelectorAll('li');
for(var i = 0; i < lis.length; i++){
(function(i){
setTimeout(function(){
console.log(lis[i].innerHTML);
},3000)
})(i);
}
</script>
计算打车的价格
打车起步价13(3公里内),之后没多一公里加5块钱,用户输入公里数就可以计算打车价格
如果有拥堵情况,总价格多收取10块钱
<script type="text/javascript">
var car = (function(){
// 起步价
var start = 13;
// 总价
var total = 0;
return {
// 正常的总价
price: function(n) {
if(n <= 3){
total = start;
} else{
total = (n - 3) * 5 + start;
}
return total;
},
// 拥堵之后的价格
yd: function(flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 33
</script>
如果 一个函数在内部可以调用其本身,那么这个函数就是 递归函数。
简单理解:函数内部自己调用自己。这个函数就是递归函数。
递归函数的作用和循环的效果一样。
由于递归很容易发生 “栈溢出” 错误(strack overflow),所以 必须要加退出条件 return。
function fn() {
fn();
// 不加退出条件就是死递归
}
fn();
var num = 1;
function fn(){
console.log('2');
if (num == 6) {
return; // 递归里面必须加退出条件
}
num++;
fn();
}
fn();
1*2*3...*n
的阶乘。function fn(n){
if(n == 1){
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
详细思路:
return 3 * f(2)
return 3 * (2 * f(1))
retutn 3 * (2 * 1)
retutn 3 * (2 )
retutn 6
求斐波那契数列(兔子序列) 1,1,2,3,5,8,13,21…
用户输入一个数字n可以求出 这个数字对应的兔子序列值
我们只需要知道用户输入的n的前面两项【(n-1)(n-2)】就可以计算出n对应序列值
function fn(n){
if(n === 1 || n === 2){
return 1;
}
return fn(n-1) + f(n-2);
}
根据用户输入的id号,就可以返回数据的对象
var data = [{
id: 1,
name: '小陈',
hobby: [{
id: 11,
name: '弹钢琴'
},{
id: 12,
name: '指弹'
}]
},{
id: 2,
name: '美美',
hobby: [{
id: 21,
name: '看小说'
},{
id: 22,
name: '海贼控'
}]
},{
id: 3,
name: '娟娟',
hobby: [{
id: 13,
name: '看小说'
},{
id: 14,
name: '海贼控'
}]
},{
id: 4,
name: '胖虎',
hobby: [{
id: 15,
name: '王者荣耀'
},{
id: 16,
name: '撩哥能妹'
}]
},{
id: 5,
name: '漫漫',
hobby: [{
id: 17,
name: '蹦迪一枝花'
},{
id: 18,
name: '桃花满怀'
}]
},{
id: 6,
name: '一菲',
hobby: [{
id: 19,
name: 'LOL控'
},{
id: 20,
name: '湖科一姐'
}]
}];
//我们想要输入id号,就可以返回数组对象
// 1 利用 forEach 去遍历里面的每一个对象
function getId(json,id){
var o = {};
json.forEach(function(item){
// console.log(item); 打印输出每个对象
// 得到数组外层的数据
if(item.id == id){
o = item;
// console.log(item);
// 得到数组里层的数据 11 12可以利用递归执行函数
// 里面应该有hobby这个数组并且数组的长度不为0
}else if(item.hobby && item.hobby.length > 0){
o = getId(item.hobby,id);
}
});
return o;
}
console.log(getId(data,21));
浅拷贝只是拷贝一层,更深层次的对象级别的只拷贝引用(修改浅拷贝后的值,拷贝前的值也会改变)
通过js中的for循环实现浅拷贝:
var obj = {
id: 1,
name: 'herat',
msg: {
age: 20,
hobby: '指弹'
}
};
var o = {};
// 通过for循环实现浅拷贝
for(var k in obj){
// k 是属性名 obj[k] 属性值
o[k] = obj[k];
}
console.log(o);
通过es6新增的方法实现浅拷贝:
Object.assign(target,…sources) es6新增的实现浅拷贝的方法
var obj = {
id: 1,
name: 'herat',
msg: {
age: 20,
hobby: '指弹'
}
};
var o = {};
Object.assign(o,obj); //把obj拷贝给o
console.log(o);
深拷贝拷贝多层,每一级别的数据都会拷贝(修改浅拷贝后的值,拷贝前的值也不会改变)
通过函数递归实现深拷贝数据:
var obj = {
id: 1,
name: 'herat',
msg: {
age: 20,
hobby: '指弹'
}
};
var o = {};
// 封装函数
function deepCopy(newobj,oldobj){
for(var k in obj){
// 判断我们的属性值属于那种数据类型
// 1 获取属性值 obj[k]
var item = obj[k];
// 2 判断这个值是否是数组(数组也属于对象类型)
if(item instanceof Array){
newobj[k] = [];
// 递归函数是把值(item)给属性(newobj[k])
deepCopy(newobj[k],item);
}
// 3 判断这个值是否是对象
else if (item instanceof Object){
newobj[k] = {};
deepCopy(newobj[k],item);
}
// 4 属于简单数据类型
else{
newobj[k] = item;
}
}
};
deepCopy(o,obj);
console.log(o);
**正则表达式(Regular Expression)**是用于匹配字符串中字符组合的模式。在Javascript中,正则表达式也是对象。
正则表达式通常被用来检索,替换那些符合某个模式(规则)的文本。例如表单验证(匹配,替换,提取)
在JavaScript中,可以通过两种方式创建一个正则表达式。
通过RegExp对象的构造函数来创建
var 变量名 = new RegExp(/表达式/);
var regexp = new RegExp(/123/);
利用字面量创建
var 变量名 = /表达式/;
var rg = /123/;
test()正则对象方法,用于检测字符串是否符合该规则,该对象会返回true和false,其参数是测试字符串。
regexObj.test(str);
regexObj 是写的正则表达式
str 我们要检测的文本
就是检测str文本是否符合我们写的正则表达式规范
一个正则表达式 可以由简单的字符构成,比如/abc/,也可以是简单和特殊字符的组合,比如/ab*c/。其中特殊字符也被称为 元字符,在正则表达式中是具有 特殊意义的专用 符号,如^,$,+等。
边界符 ^ $
// 只要包含abc这个字符串都会返回true
var reg = /abc/; //正则表达式里面不需要加引号 不管是数字型还是字符型
var reg = /^abc/; //匹配以abc开头的字符
var reg = /^abc$/; //匹配以abc开头,以abc结尾的字符
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
如果^和$在一起,表示必须是精确匹配。 |
字符类:[] 表示有一系列字符可供选择,只要匹配其中一个就可以了
var reg = /[abc]/; //只要包含有a 或者 包含有b 或者 包含有c的字符,都返回为true
var reg = /^[abc]$/; //三选一,只有是a或者b或者c,就返回true
var reg = /^[a-z]$/; //26个英文字母任何一个字母都返回true
var reg = /^[a-zA-Z0-9_-]{3,16}$/ //3到16位(用户名)
var reg = /^[a-zA-Z0-9_-]{6,18}$/ //密码
var reg = /^[^a-zA-Z0-9_-]{6,18}$/ //如果中括号里面有^,表示取反的意思
量词符
量词符用来 设定某个模式出现的次数。
量词 | 说明 |
---|---|
* | 重复零次或更多次(>=0) |
+ | 重复一次或更多次(>=1) |
? | 重复零次或一次 [0,1] |
{n} | 重复n次 (=n) |
{n,} | 重复n次或更多次 (>=n) |
{n,m} | 重复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] |
功能需求:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style type="text/css">
span {
color: gray;
font-size: 14px;
}
.right {
color: green;
}
.wrong {
color: red;
}
</style>
</head>
<body>
<input type="text" class="username"><span>请输入用户名</span>
<script type="text/javascript">
var reg = /^[a-zA-Z0-9_-]{6,16}$/;
var username = document.querySelector('.username');
var span = document.querySelector('span');
// 失去焦点事件
username.onblur = function() {
if (reg.test(this.value)) {
// 输入的值符合正则规范
// console.log('输入正确');
// 给提示的span添加样式
span.className = 'right';
span.innerHTML = '用户名输入格式正确';
} else {
// console.log('输入错误');
// 给提示的span添加样式
span.className = 'wrong';
span.innerHTML = '用户名输入格式错误';
}
}
</script>
</body>
</html>
验证座机号码
座机号码验证:全国座机号码 两种格式:010-12345678 或者 0530-1234567
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<script type="text/javascript">
// 注意:
// 正则里面的 或者 用 | 表示
// 正则表达式里面不能存在空格
var reg = /^\d{3}-\d{8}|^\d{4}-\d{7}$/;
// 简写
var reg = /^\d{3,4}-\d{8,7}$/;
</script>
</body>
</html>
// 中括号 字符集合,匹配方括号中的任意字符
var reg = /^[abc]$/; //类似于 a || b || c
//大括号 量词符,里面表示重复数
var reg = /^a{3}$/; //a重复了3次
var reg = /^abc{3}$/; //abccc c重复了3次
//小括号 表示优先级
var reg = /^(abc){3}$/; //abcabcabc abc重复3次
replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
stringObject.replace(regxp/substr,replacement);
第一个参数:被替换的字符 或者 正则表达式
第二个参数:替换的字符串
返回的是一个替换完毕的新字符串
var str = 'amy and mike';
//通过字符串替换
var newStr = str.replece('amy','joo');
//通过正则表达式替换
var newStr = str.replece(/amy/,'joo');
console.log(str); // 打印输出的str为 joo and mike
```javascript
/表达式/[switch]
switch(也称为修饰符),按照什么样的模式来匹配,有三种值:
什么是ES6?
ES的全称是ECMAScript,它是由ECMA国际标准化组织,指定的 一项脚本语言的标准化规范。
年份 | 版本 |
---|---|
2015年6月 | ES2015 |
2016年6月 | ES2016 |
2017年6月 | ES2017 |
2018年6月 | ES2018 |
…… | …… |
ES6实际上是一个泛指,泛指2015及后续版本。
每次标准的诞生都意味着语言的完善,功能的强大。JavaScript语言本身也有一些令人不满意的地方。
ES6中新增的用于声明变量的关键字。
let声明的变量只在所处于的块级有效
使用let关键字声明的变量具有块级作用域(就是一队大括号产生的作用域)
if (true) {
let a = 10;
}
console.log(a); //a is not defined
块级作用域的好处是:在业务逻辑比较复杂的时候,避免内层变量覆盖外层变量,防止了循环变量,变成全局变量。
注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具有块级作用域。
不存在变量的提升
console.log(a); // a is not defined
let a = 10;
暂时性死区
var tmp = 123;
if (true) {
console.log(tmp); //tmp is not defined
let tmp = 456;
}
作用:声明常量,常量就是值(内存地址)不能变化的量。
具有块级作用域
if (true) {
const a = 10;
}
console.log(a); // a is not defined
使用const关键字声明常量时必须赋值
const PI; //Missing initializer in const declaration
常量赋值后,值不能修改
基本数据类型:值不能更改
const PI;
PI = 3.14; //Assignment to constant variable.
复杂数据类型:数据结构内部的值可以更改,但是数据本身不能被更改
const ary = [100,200];
ary[0] = 20;
// 对于复杂类型来说 里面的值是可以更改的
console.log(ary); //打印输出[20,200]
ary = [1,3];
// 但是不能 重新赋值(更改了常量值的内存地址)
console.log(ary); // Assignment to constant variable.
优点:防止重复声明,限制修改
let:不能重复声明,变量,块级
const:不能重复声明,常量,块级
使用var
声明的变量,其作用域为 该语句所在的函数内,且存在变量提升现象。
<button>1</button>
<button>2</button>
<button>3</button>
<script type="text/javascript">
window.onload = function() {
let btn = document.getElementsByTagName('button');
for (var i = 0; i < btn.length; i++) {
btn[i].onclick = function() {
alert(i); // 使用var关键字,点击每个按钮都会弹出3
}
}
// 弹出3的原因是,循环结束的条件是 3
// 事件是异步的,所以只能的到最后的索引值
}
</script>
通过函数发的方式修改:
for (var i = 0; i < btn.length; i++) {
(function(){
btn[i].onclick = function() {
alert(i);
}
})(i);
}
使用let
声明的变量,其作用域为 该语句所在的代码块内,不存在变量的提升。
<button>1</button>
<button>2</button>
<button>3</button>
<script type="text/javascript">
window.onload = function() {
// 2 将var 改为 let
for (let i = 0; i < btn.length; i++) {
btn[i].onclick = function() {
alert(i); // 使用let关键字,点击那个按钮就弹出那个按钮的索引
}
}
}
</script>
使用const
声明的变量,在后面出现的代码中 不能在修改该常量的值。
var | let | const |
---|---|---|
函数级作用域 | 块级作用域 | 块级作用域 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
ES6中允许从数组中取值,按照对应位置,对变量赋值。对象也可实现解构。
变量的值和数组的值数量是一样:
let [a,b,c] = [1,2,3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
不一样:
如果解析不成功,变量的值为undefined
let foo = [];
let [a,b] = [1];
console.log(a); // 1
console.log(b); // undefined
arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(arr); //打印输出 [1, 2, 3]
解构赋值
按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量。
使用变量去匹配属性:
let person = { name: 'zhangsan', age: 20};
let { name, age } = person;
console.log(name); // zahngsan
console.log(age); // 20
let person = { name: 'zhangsan', age: 20};
let { name: myName, age: myAge } = person; //myName,myAge属于别名
console.log(myName); //zhangsan
console.log(myAge); //20
两边的结构必须一样
let {a, b, c} = [1, 2, 3]; //错误
//必须为
let {a, b, c} = {x, x, x}
右边必须是合法
arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(arr); //打印输出 [1, 2, 3]
赋值和解构必须同时完成
let {a, b};
console.log({a, b} = {a: 12,b: 15});
// 错误 Missing initializer in destructuring declaration
//正确写法
let {a, b} = {a: 12,b: 15};
console.log({a, b}); // 打印输出 {a: 12, b: 15}
ES6中新增的定义函数的方式。
() => {}
// 箭头函数的调用
// 将函数赋值给一个变量,然后通过变量名来调用
const fn = () => {
console.log(123)
}
fn(); //在控制台打印输出 123
// 普通函数
function sum(num1, num2){
return num1 + num2;
}
// 箭头函数
const sum = (num1, num2) => num1 + num2;
const result = sum(2, 3);
console.log(result); // 浏览器控制台输出 5
// 普通函数
function fn(v){
return v;
}
//箭头函数
const fn = v => { alert(v); };
fn(20);
排序:
let arr = [1,4,55,9,2,0];
console.log(arr.sort()); // 打印输出 [0, 1, 2, 4, 55, 9] 他是把数值当成字符来排序的
// 普通函数
arr.sort(function(n1,n2){
return n1 - n2;
});
console.log(arr); //打印输出 [0, 1, 2, 4, 9, 55]
// 箭头函数 () => {}
arr.sort((n1,n2) => { n1 - n2; });
console.log(arr); //打印输出 [0, 1, 2, 4, 9, 55]
根据分数判断评语:
let arr = [45,60,90,100];
// 通过if来判断
let arr2 = arr.map(function(item){
if (item >= 60) {
return '及格';
} else {
return '不及格';
}
});
// 通过三元表达式来判断
let arr2 = arr.map(item){
return item >= 60 ? '及格':'不及格';
}
// 箭头函数
let arr2 = arr.map(item >= item >= 60 ? '及格':'不及格');
console.log(arr); // [45, 60, 90, 100]
console.log(arr2); // ["不及格", "及格", "及格", "及格"]
求总和和平均数:
let arr = [45, 60, 90, 100, 75, 67];
let result = arr.reduce(function (tmp, item, index) {
// console.log('第'+ index + ':' + tmp + ',' +item);
// 第1:45,60,第2:undefined,90,第3:undefined,100,第4:undefined,75,第5:undefined,67
return tmp + item;
});
console.log('和为:' + result); // 打印输出437
console.log('平均数为:' + result / arr.length); //平均数为:72.83333333333333
平均数:
let arr = [45, 60, 90, 100, 75, 67];
let result = arr.reduce(function (tmp, item, index) {
if(index == arr.length -1){ //最后一次
return (tmp + item)/arr.length;
} else {
return tmp + item;
}
});
console.log(result); // 打印输出72.83333333333333
筛选偶数:
let arr = [45, 60, 90, 100, 75, 67];
// 1 通过if判断来筛选
let newArry = arr.filter(item => {
if(item % 2 === 1){
return false;
} else {
return true;
}
});
// 2 通过三元表达式来筛选
let newArry = arr.filter(item => item % 2 === 0);
console.log(newArry); //[60, 90, 100]
let arr = [45, 60, 90, 100, 75, 67];
// 第0个值是45,第1个值是60,第2个值是90,第3个值是100,第4个值是75,第5个值是67
arr.forEach((item, index) => console.log('第' + index + '个值是' + item));
// 通过字符串模板(反单引号)来遍历
arr.forEach((item, index) => console.log(`第${index}个值是${item}`));
箭头函数不绑定 this 关键字,箭头函数中的this,指向的是 函数定义位置的上下文this。
箭头函数被定义在哪儿,this关键字就指向哪儿。
箭头函数不绑定this,箭头函数没有自己的this关键字,如果在箭头函数中使用this,this关键字将指向箭头珊瑚定义位置中的this。
const obj = { name: '小陈' };
function fn () {
console.log(this); // Object
// 匿名函数
return () => {
console.log(this);// Object
}
}
// call方法可以改变函数内部的this指向
const resFn = fn.call(obj); //使用call方法调用fn函数,将函数的this指向了obj对象
resFn();
// Identifier 'json' has already been declared
// 注意: class 命名必须为以大写字母开头
class Json {
constructor(){
console.log(this); // 打印输出 Json {} 对象
this.a = 12;
this.b = 8;
this.fn = function() {
console.log(this.a); // 打印输出 12
}
this.fnn = function() {
console.log(this.b); // 打印输出 8
}
}
}
// 实例化Json类
let json = new Json();
// 调用Json里面的fn方法
json.fn();
// 调用Json里面的fnn方法
json.fnn();
对象是不能产生作用域的
var obj = {
age: 20,
// 箭头函数没有this
say: () => {
console.log(this.age); //打印输出undefined
}
}
// obj是一个对象,不能产生作用域,所以箭头函数实际上被定义在全局作用域下,所以调用时,this指向的window,window没有age属性,所以打印输出的是undefined
obj.say();
参数展开:收集剩余参数,展开信息。
剩余参数语法允许我们将一个不定量的参数表示为一个数组。
当函数实参大于新参时,可以把大于的实参放在数组中。
function sum(first, ...args){
console.log(first); // 10
console.log(args); // [20,30]
}
sum(10, 20, 30);
计算所传值的和:
const sum = (...args) => {
let total = 0;
//args.forEach(item => {
// total += item;
//});
args.forEach(item => total += item);
//将计算的值返回回去
return total;
};
sum(10, 20);
sum(10, 20, 30);
let are = [14, 12, 11];
function show(a, b, c){
console.log(a + b + c);
}
show(are); // 打印输出 14,12,11undefinedundefined ,相当于只传了一个参数
show(...are); // 打印输出 37
json的合并:
let json = {a: 12, b: 15};
let json2 = {
...json,
c: 20
}
console.log(json2);
剩余参数和解构配合使用:
let students = ['yanyan' , 'meimei', 'juanjuan'];
let [s1,...s2] = students;
console.log(s1); // 'yanyan'
console.log(s2);` // ['meimei', 'juanjuan']
JSON的标准写法:{“a”:2,“b”:3},在js中可以简写{a: 2, b:3}。
JSON.stringify()
传入json值,返回来的是字符串
var js = JSON.stringify({a: 2, b:3});
console.log(js); // 打印输出 {"a":2,"b":3}
JSON.parse()
将字符串解析为json
var js = JSON.parse('{"a":2,"b":3}');
console.log(js); //打印输出{a: 2, b: 3}
扩展运算符(展开语法)
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。
let ary = [1, 2, 3];
//...ary //1 2 3 也就是将ary数组的中括号去掉了
console.log(...ary);
扩展运算符可以应用与 合并数组
// 方法一
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2];
console.log(ary3); // 打印输出[1, 2, 3, 4, 5, 6]
// 方法二
//通过push方法,将ary2中的元素追加到ary1中
ary1.push(...ary2);
console.log(ary1); // 打印输出[1, 2, 3, 4, 5, 6]
扩展运算符可以将类数组或可遍历的对象转换为真正的数组
let d = document.getElementsByTageName('div');
d = [...d];
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script type="text/javascript">
var d = document.getElementsByTagName('div');
// 现在的d是一个伪数组
// console.log(d);
//将伪数组转换成一个真正的数组
const ds = [...d];
console.log(ds);
// 将伪数组转换为真正的数组后,就可以使用数组的方法
ds.push('a');
console.log(ds);
</script>
</body>
</html>
将类数组或可遍历的对象转换成真正的数组。
let array = {
'0':'a',
'1':'b',
'2':'c',
'3':'d',
'4':'e',
length:5 //注意:在伪数组中一定要写length,不然遍历出来的值为[]
};
let arry2 = Array.from(array);
console.log(arry2); //["a", "b", "c", "d", "e"]
方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值返回数组。
let array = {
'0':'1',
'1':'2',
'2':'3',
'3':'4',
'4':'5',
length:5 //注意:在伪数组中一定要写length,不然遍历出来的值为[]
}
let arry2 = Array.from(array, item => item * 2);
console.log(arry2); // [2, 4, 6, 8, 10]
用于找出第一个符合条件的数组成员,如果没有找到返回undefined。
let array = [{
id: 1,
name: '小陈'
},{
id: 2,
name: '小王'
},{
id: 3,
name: '小姜'
},{
id: 4,
name: '小唐'
},{
id: 5,
name: '小黎'
},{
id: 6,
name: '小肖'
}];
// item 代表的是当前循环的值,index代表的是当前循环的索引
// let target = array.find((item, index) => item.id ==2);
let target = array.find((item) => item.id ==2);
console.log(target); //打印输出 {id: 2,name: '小王'}
用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1。
var ary = [1, 3, 5, 7, 9];
let index = ary.findIndex((value) => value > 5);
console.log(index); //打印输出 3
表示某个数组是否包含给定的值,返回的是布尔值。
//js
console.log([1, 2, 3].indexOf(3) != -1); // 返回true
//es7中includes方法
[1, 2, 3].includes(2); //true
模板字符串
ES6新增的创建字符串的方式,使用反引号定义。
let name = `xiaochen`;
特点:
模板字符串可以 解析变量
let name = `xiaochen`;
let sayHello = `hello,my name is ${name}`;
console.log(sayHello); // 打印输出 hello,my name is xiaochen
模板字符串可以 换行
let result = {
name: 'xiaochen',
age: 20,
sex: '女'
}
let html = `
${result.name}
${result.age}
${result.sex}
`;
console.log(html);
模板字符串可以 调用函数
const sayHello = function(){
return 'hello world';
}
// 在变量中调用函数
let get = `${sayHello()}`;
console.log(get); //打印输出 hello world
startsWith():表示是参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
let str = 'hello world';
let r1 =str.startsWith('hello');
console.log(r1); // true
let r2 = str.endsWith('world');
console.log(r2); // true
repeat方法表示将原字符串重复n次,返回一个新的字符串
let x = 'x'.repeat(3);
console.log(x); //打印输出 xxx
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
const s = new Set();
console.log(s.size); //打印输出0,证明数据结构为空
Set构造函数可以接受一个数组作为参数,用来初始化。(数组中的值会被自动存储在Set数据结构中)
const set = new Set([1, 2, 3, 4]);
console.log(set.size); //打印输出4,证明在当前数据结构中存储了4个值
当我们使用Set,向里面传值时,会把重复的值过滤掉。
const s = new Set([1, 1, 2]);
console.log(s.size); //打印输出2
可以利用Set的这个属性做数组去重。
const s = new Set([1, 1, 2]);
console.log(s.size); //打印输出2
const ary = [...s]; // 通过扩展运算符得到新的数组
console.log(ary); //打印输出 [1, 2]
add(value):添加某个值,返回Set结构本身
delete(value):删除某个值,返回一个布尔值,表示是否删除成功
has(value):返回一个布尔值,表示该值是否为Set的成员
clear():清除所有成员,没有返回值
const s = new Set();
// 向Set结构中添加值
s.add(1).add(2).add(3);
console.log(s); //打印输出 {1, 2, 3}
// 删除Set结构中的2
s.delete(2);
console.log(s); //打印输出 {1, 3}
// 判断Set结构中有没有1 返回的是布尔值
console.log(s.has(1)); // 打印输出 true
// 清除Set结构中所有的值
s.clear();
console.log(s); // 打印输出 {}
Set结构的实例与数组一样,也拥有forEach方法,用于每个成员执行某种操作,没有返回值。
s.forEach(value => console.log(value));
const s5 = new Set(['小小','大大','跳跳','悄悄']);
s5.forEach((value) => {
console.log(value); //打印输出小小 大大 跳跳 悄悄
});
用法:解决低版本浏览器不兼容es6的问题
缺点:会延迟,会卡,浏览器低版本不认识(babel本身就不兼容低版本浏览器)
<!-- 引入browser.min.js文件 -->
<script src="browser.min.js" charset="utf-8"></script>
<!-- 在script标签上面添加 type="text/babel" -->
<script type="text/babel">
let a = 12;
let show = n => alert(n);
show(a);
</script>
安装Node.js,初始化项目
创建package.json文件
npm init -y
2.安装babel-cli
npm i @babel/core @babel/cli @babel/preset-env -D
npm i @babel/polyfill -S
添加执行脚本(package.json中)
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel src -d dest" //通过babel来编译src这个目录,输出dest
}
添加.babelrc
配置文件
{
"presets": [
// 声明预设
"@babel/preset-env"
]
}
执行编译
npm run build
异步操作(ajax):
优点:可以同时进行多个操作,用户体验良好
缺点:代码混乱,编写麻烦
ajax('www.baidu.com',function (data) {
ajax('www.baidu.com',function (data) {
...
},function (data) {
console.log("编译失败");
})
},function () {
console.log("编译失败");
});
同步操作:
优点:代码清晰,编写简单
缺点:一次只能进行一个操作,用户体验不好
let data1 = ajax('http://baidu.com');
...
融合异步操作和同步操作的优势:Promise,async/await
注意:jquery本身就是一个Promise,有返回值then
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<script src="styles/jquery.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<script type="text/javascript">
// 对异步操作做一个统一封装
let p = new Promise(function(resolve, reject) {
// resolve 解决/成功
// reject 拒绝/失败
$.ajax({
url: 'data/1.txt',
dataType: 'json',
success(arr) {
resolve(arr);
},
error(res) {
reject(res);
}
});
});
// 调用
p.then(function(arr) {
// 成功
console.log('成功');
console.log(arr);
},function(res) {
console.log('失败');
console.log(res);
});
</script>
</body>
</html>
Promise.all([
$.ajax(url: 'data/1.txt', dataType: 'json');
$.ajax(url: 'data/2.txt', dataType: 'json');
$.ajax(url: 'data/3.txt', dataType: 'json');
]).then(arr => {
// 输出的是上面三个文件里面的所有内容
// 需要上面三个文件都成功,然后就执行成功操作
console.log(arr);
//可以通过解构解析,取出某一个文件中的值
[data1,data2,data3] = arr;
console.log(data2); //打印输出data2中的数据
},res => {
console.log('失败')
});
除非都失败了才走error
通俗的说async和await是函数的一种形式。
async是一种语法,使用时直接加载function的前面。
async function show(){
...
// 等待操作结束后再继续往下执行
let data = await $.ajax();
}
普通函数 和 async函数区别:
普通函数————一直执行,直到结束
async函数————能够"暂停"-“await”
// 实际上async的执行过程(解析成单个来解析的)
// 写起来更方便,但是执行还是异步的
// function show1() {
// let a = 12;
// let b = 5;
// }
// let data = await $.ajax({url: 'data/1.txt', dataType: 'json'}).then(function(){
// show2();
// });
// function show2() {
// console.log(a + b + data[0]); //打印输出 18
// }
async function show() {
let a = 12;
let b = 5;
//使用try catch来捕获错误
try{
let data = await $.ajax({url: 'data/1.txt', dataType: 'json'});
console.log(a + b + data[0]); //打印输出 18
} catch(e) {
console.log(e);
}
}
show();
机器语言 -> 汇编语言 -> 低级语言(面向过程)-> 高级语言(面向对象)-> 模块系统 -> 框架 -> 系统接口(API)
// 没有专门的类声明方法,类是通过函数实现的
// 1.既是构造函数,又是类
// 是以函数的方式来写对象
function Person(name, age){
this.name = name;
this.age = age;
}
// 2.方法独立在类之外
// 给类添加方法
Person.prototype.show = function (){
alert(this.name);
}
// 3.没有专门的继承方法
function worker(name, age, job) {
Parson.call(this, name, age); // 4.从父类继承数据
this.job = job;
}
// 5.没有专门继承父类的方式
// 创建类的实例
worker.prototype = new Person();
worker.prototype.constructor = worker;
worker.prototype.Job = function (){
console.log(this.job);
};
let w = new worker('xiaochen', 18, '程序员');
统一了写法,出现了类class
,构造函数constructor
,继承extends
,超类/父类super
的声明。
继承的优点:省事儿(父类有的直接用),便于扩展(出现错误只需要修改一处)。
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
show(){
console.log(this.name);
}
}
class work extends Person{
constructor(name, age, job){
super(name,age);
this.job = job;
}
showjob(){
console.log(this.job);
}
}
let w = new work('xiaochen', 12, '程序员');
w.showjob();
没有模块 -> CMD(Common Model Definition)[民间模块系统,但不受官方支持,模块必须加载完后再执行] -> AMD(Asynchronous Module Definitious) [异步模块,模块加载自由,不受官方支持] -> 语言提供模块支持(ES6)
浏览器现在不支持ES6的模块系统,但是为了使用模块系统,就出现了webpack
webpack.config.js中:
// 引入path包
const path = require('path');
module.exports = {
// 告诉浏览器编译的模式是什么
mode: 'production',
// 入口
entry: './index.js',
// 输出 or 编译结果
output: {
// 放在哪儿去
path: path.resolve(__dirname,'build');
// 文件名
filename: 'build.js';
}
};
导出(export)
// 导出一个变量
export let a = 12;
// 导出多个变量
let a,b,c = ...;
export {a, b, c};
// 导出函数
export function show(){
}
// 导出class
export class Promise{}
// 导出默认成员
export default a...
导入(import)
import * as mod from 'XXX'; // 引入所有成员
import mod from 'XXX'; // 引入default成员
import {a,b} from 'XXX'; // 引入a,b变量
import 'XXX'; //引入整个文件
let p = import('./mode'); //异步引入,使用什么就引入什么(当函数使用的)
webpack编译
es7中的 幂操作 和 includes方法
// 求三的五次方
console.log(Math.pow(3, 5)); // 打印输出243
// 在es7中求三的五次方
console.log(3 ** 5); // 打印输出243
es7中的includes方法
es8中 async/await
,但是执行还是异步的
// function show1() {
// let a = 12;
// let b = 5;
// }
// let data = await $.ajax({url: ‘data/1.txt’, dataType: ‘json’}).then(function(){
// show2();
// });
// function show2() {
// console.log(a + b + data[0]); //打印输出 18
// }
async function show() {
let a = 12;
let b = 5;
//使用try catch来捕获错误
try{
let data = await $.ajax({url: 'data/1.txt', dataType: 'json'});
console.log(a + b + data[0]); //打印输出 18
} catch(e) {
console.log(e);
}
}
show();
## ES6面向对象
### 语言发展历史
机器语言 -> 汇编语言 -> 低级语言(面向过程)-> 高级语言(面向对象)-> 模块系统 -> 框架 -> 系统接口(API)
### ES5中的面向对象
1. es5中的面向对象是一个假的面向对象
2. 没有统一的写法(处于一个自我摸索的阶段)
```javascript
// 没有专门的类声明方法,类是通过函数实现的
// 1.既是构造函数,又是类
// 是以函数的方式来写对象
function Person(name, age){
this.name = name;
this.age = age;
}
// 2.方法独立在类之外
// 给类添加方法
Person.prototype.show = function (){
alert(this.name);
}
// 3.没有专门的继承方法
function worker(name, age, job) {
Parson.call(this, name, age); // 4.从父类继承数据
this.job = job;
}
// 5.没有专门继承父类的方式
// 创建类的实例
worker.prototype = new Person();
worker.prototype.constructor = worker;
worker.prototype.Job = function (){
console.log(this.job);
};
let w = new worker('xiaochen', 18, '程序员');
统一了写法,出现了类class
,构造函数constructor
,继承extends
,超类/父类super
的声明。
继承的优点:省事儿(父类有的直接用),便于扩展(出现错误只需要修改一处)。
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
show(){
console.log(this.name);
}
}
class work extends Person{
constructor(name, age, job){
super(name,age);
this.job = job;
}
showjob(){
console.log(this.job);
}
}
let w = new work('xiaochen', 12, '程序员');
w.showjob();
没有模块 -> CMD(Common Model Definition)[民间模块系统,但不受官方支持,模块必须加载完后再执行] -> AMD(Asynchronous Module Definitious) [异步模块,模块加载自由,不受官方支持] -> 语言提供模块支持(ES6)
浏览器现在不支持ES6的模块系统,但是为了使用模块系统,就出现了webpack
webpack.config.js中:
// 引入path包
const path = require('path');
module.exports = {
// 告诉浏览器编译的模式是什么
mode: 'production',
// 入口
entry: './index.js',
// 输出 or 编译结果
output: {
// 放在哪儿去
path: path.resolve(__dirname,'build');
// 文件名
filename: 'build.js';
}
};
导出(export)
// 导出一个变量
export let a = 12;
// 导出多个变量
let a,b,c = ...;
export {a, b, c};
// 导出函数
export function show(){
}
// 导出class
export class Promise{}
// 导出默认成员
export default a...
导入(import)
import * as mod from 'XXX'; // 引入所有成员
import mod from 'XXX'; // 引入default成员
import {a,b} from 'XXX'; // 引入a,b变量
import 'XXX'; //引入整个文件
let p = import('./mode'); //异步引入,使用什么就引入什么(当函数使用的)
webpack编译
es7中的 幂操作 和 includes方法
// 求三的五次方
console.log(Math.pow(3, 5)); // 打印输出243
// 在es7中求三的五次方
console.log(3 ** 5); // 打印输出243
es7中的includes方法
es8中 async/await
es9中 rest/spread消除异步操作,及 异步迭代,Promise.all/race/finally
学习资料推荐:https://es6.ruanyifeng.com/