一、面向对象编程
-
对象:包括字符串 数组 自定义对象等,万物皆对象
对象是单个事物的抽象,包含属性和行为(方法,功能)
一个容器,封装了属性和方法,面向对象,就是将属性和方法封装到几个对象中;
要用的话,直接调用就可以
面向对象的特性:封装性 继承性 【多态性】抽象
2.打印学生的成绩,使用对象的方法
3.面向对象的设计思想
抽象除Class(构造函数) 根据Class(构造函数)创建Instance(实例) 指挥Instance得结果
4.创建对象的几种方式
方法1.
var a=new Object(); 这样有一点麻烦
a.name="Bob";
a.age=18;
a.sayName=function(){
}
方法2.
var a={name:"Bob";age:18;sayName:function(){ } }
字面量方法容易重复
方法3.封装一个工厂函数
方法4.自定义构造函数
5.实例成员:在构造函数内部添加给this的成员,在创建实例是必须由对象调用
静态成员:添加构造函数自身的成员,只能由构造函数调用 如: Math.PI 调用圆周率
6.多个对象中有公共的属性,每次调用会浪费内存,解决方法一:
将公共的函数提前到构造函数之外:
function sayName(){`
`console.log(this.name);`
`}`
`function Person(name,age,sex) {`
this.name=name; --用this替代新对象person,不需要创建一个空对象Object`
this.age=age;
this.type=human;
this.sayName=sayName;}
`var p1= new Person(“zs”,18,true);`
`p1.sayName ();`
解决方法二:将公共的函数封装到一个对象中;然后在后面继续写构造函数
var fns={sayName:function() {
console.log(this.name)};
sayAge:function() {
console.log(this.age)};
}
7.解决方法三:使用原型对象:prototype对象,可以解决内存浪费问题
1.任何函数都有prototype属性:构造函数也不例外,属性值是一个对象,通常叫做
原型对象,这个对象内部可以添加一些属性和方法:比如上面构造函数Person进行操作
添加属性和方法:
Person.prototype.type="human" ;Person.prototype.sayHi=function(){ 函数内容};
构造函数的原型对象的constructor属性(对象都有这个属性):指向自己Person这个构造函数:
console.log(**Person.prototype.constructor** )
输出为Person这个函数
8.所有的对象都有——proto——_的属性:是一个指针,指向的就是生成实例对象的构造函数的原型对象,即 实例对象的——proto——属性指向的是这个实例对象的原构造函数的prototype属性
——proto——属性不是一个标准的属性,是浏览器自己根据语法自动生成的
p1.——proto——.saiHi()=Person.prototype.saiHi() ,不过一般简写成p1.sayHi() 即可
同样p1.——proto——.constructor=p1.constructor ,输出是构造函数Person
9.每个构造函数的prototype属性指向他的原型对象,而这个原型对象的所有属性和方法,都会被构造函数的实例对象所拥有,因此:
我们可以把实例对象需要共享的属性和方法直接定义在prototype对象上,优化解决方法二:将所有实例共享的属性和方法,如之前构造函数中的type=human属性 以及sayName 方法,都添加给原型对象:
Person.prototype.type=human;
Person.prototype.sayName=function(){console.log(this.name)};
p1.sayName();
//调用即可
Person构造函数的原型对象也是对象,是对象就有原型对象,所以生成的实例对象的原型对象的原型对象是一个叫做Object函数的原型对象,而这个Object的原型对象指向的是一个空指针null;关系图,也就是原型链:10.原型链:
12.实例对象读写原型对象成员
先在自己身上找,找不到的话会沿着原型链向上查找,如果一直到原型链的末端都没有找到,则返回undefined;
通过实例对象更改或添加原型对象的属性和方法,会直接添加给自己,会屏蔽对原型对象的访问
通过实例对象更改或添加原型对象中复杂类型的数据,如果自己没有找到,会沿着原型链继续查找:
如添加一个性别属性 p1.sex="male"
,然后console.dir(p1)
会在自己身上找到,但是在自己的原型对象之上没有这个新加的属性 ;
如添加一个方法: p1.sayAge=function(){} 同样输出console.dir(p1)
,这个方法只添加在自己身上
如果通过实例对象更改原型对象的属性和方法:
p1.type="person"
(原来是 human),同样输出console.dir(p1)
, 发现这个实例对象增加了一个type="person"的属性,而他的原型对象p1.--ptoto--
上的type="human"还是没有变化,添加方法也一样的
如果添加一个新的属性给原型对象,是一个对象类型,city:beijng,
如:``
Person.prototype.address={city:"beijing"}
通过实例对象更改:
p1.address.city="shanghai "
会在原型链中进行查找,
输出p1这个实例对象,会发现在自己身上没有修改,而是修改到p1的实例对象p1.--proto--
上了
13.更简单的原型语法
每添加一个属性和方法都要写一Person.prototype
,比较麻烦,因此使用一个对象字面量 对
对象原型进行赋值 ,如:
Person.prototype={
type:"human";
sayName:function(){console.log(this.name)}
}
这样会丢失constractor
成员,因此要手动添加contractor属性指向正确的构造函数
Person.prototype`={
type:"human",
constractor:Person, 手动添加contractor属性指向正确的构造函数
sayName:function(){console.log(this.name)};}
一般将私有成员(非函数)放到构造函数中,将公共成员(一般是函数)放到原型对象中,充
重置了Person.prototype
记得手动添加contractor属性指向正确的构造函数
14.内置构造函数的原型对象
Object.prototype Function.prototype String.prototype Array.prototype
一般不允许更改内置而构造函数的原型对象
15.自调用函数IIFE:表示函数在定义时就立即调用,如果将一个函数矮化成一个表达式,就可以在后面添加()进行调用
通过在函数前面添加操作符,可以让函数矮化成表达式 如+ - ()! 四种方式、
最常用的是(),将函数用()包裹起来,可以关住函数的作用域,在外部是不能进行
调用的
(function(a) {
console.log(a)} (1); 用完之后后面就用不了了,类似于一次性函数,后面括号里
是实参,输出为 1
补充:函数调用的方式,函数名或函数表达式后加(),自调用函数就是一个函数表达式加了
(),这个函数表达式用()把原函数矮化成立表达式
自调用函数可以封闭作用域
二、随机方块
1.以对象字面量的方式,封装一个tools对象,里面添加获取随机整数的方法getRandom,
,MDN上有,直接用
在添加一个获取随机颜色的方法:给rgb(r,g,b)
中的r g b获取0-255的随机值,调用getRandom
方法,用this调用,最后返回一个颜色值: return "rgb("+r+""+g+""+b+")"
2.自定义一个 随机方块 的构造函数,设置方块的宽高 背景颜色 定位位置
function Block( option){ 参数option是一个对象,里面包含宽高 颜色 定位等方块的属性
option=option||{ };
this.width=option.width||20;
this.height=option.height||20;
this.backgroundColor=option.backgroundColor||"red";
this.x=option.x||0;
this.y=option.y||0;
}
三、贪吃蛇
1.创造Food的构造函数,设置宽高 位置 颜色 的属性
通过原型设置方法,将事物渲染到大的盒子map上
onkeydown
按键按下事件 键盘编码 37:左键
38:上键 39:右键 40:下键
window.Food=Food
利用window对象增加一个food属性,属性值就是food的构造函数,可以把food 的构造函数在外部进行调用,在外部生成对象实例
2.思路:根据游戏抽象出几个必要的对象,Food Snake Game对象,其中Game对象并不是游戏中的必备元素,而是游戏的逻辑,都可以抽象出对象。
3.Food 对象:构造函数生成对象,设置宽高、left、top的基本属性;在其原型对象上添加渲染render方法,使其能够随机出现在地图中,需要使用空数组存储所有会出现的Food,保障后期随时生成和删除;
同样在其原型对象上添加删除remove方法。
function Food(){
// 自身高度,宽度,默认的位置,颜色
this.width=20;
this.height=20;
this.x=0;
this.y=0;
this.color="green";
// 创建一个空数组容纳随机生成的食物块
this.elements=[];
}
4.Snake 对象:构造函数生成对象,注意设置的每一个蛇节的css属性,在其原型对象上添加渲染方法,注意是一节一节渲染,初始3节,循环渲染,渲染后需要删除重新渲染;
Snake.prototype.render=function(map){
// 循环的方法进行渲染
for (var i = 0,len = this.body.length ; i < len ; i++){
var ele=document.createElement("div");
// 给生成的每一个ele添加样式;
ele.style.width = this.width+"px";
ele.style.height=this.height+"px";
ele.style.left=this.body[i].x*this.width+"px";
ele.style.top=this.body[i].y*this.height+"px";
ele.style.backgroundColor=this.body[i].color;
ele.style.position= "absolute";
map.appendChild(ele);
this.elements.push(ele);
}
};
在原型对象上添加位置食物方法 move,及初始方向向右移动,当前一节的位置left top等于前一节的位置
for(var i=this.body.length-1;i>0;i--){
this.body[i].x=this.body[i-1].x;
this.body[i].y=this.body[i-1].y;
}
5.Game对象,最重要的一步,决定游戏是否成功运行的关键,需要首先在其构造函数中设置Food 和Snake属性,生成相应的对象实例,后续直接调用。
var that;
function Game(map){
// 生成实例对象作为属性
this.food = new Food();
this.snake = new Snake();
this.map = map;
that = this;
}
需要用到的方法:绑定按键的bindKey:给整个文档doucument设置 onkeydown事件,利用事件委托函数控制蛇的移动方向
让蛇运动的方法:设置定时器,定时器函数参数包含 Food实例和Snake直接调用的render 和remove函数,另外需要判断蛇吃到食物的条件,以及吃到食物后,将蛇节最后一一项添加给蛇身自己。
// 吃掉食物增加一个蛇节
// 吃掉食物的条件:蛇头的位置和食物的位置重合
for(var i=0;i<that.food.elements.length;i++){
if(hX===that.food.elements[i].offsetLeft&&hY===that.food.elements[i].offsetTop){
that.food.remove(that.map,i);
that.food.render(that.map);
var last=that.snake.body[that.snake.body.length-1];
that.snake.body.push({
x:last.x,
y:last.y,
color:last.color
});
}
}
四、继承
1.对象之间的继承,父级对象继承到子级的对象,如 老李和小李两个对象
var laoli = {
name: "laoli",
money: 1000000,
house: ["商铺", "住宅"],
tech: function () {
console.log("厨艺")
}};
var xiaoli={
name: "xiaoli"
对象之间进行继承,使用 for……in
for (var k in laoli) {
子级有的属性不需要继承
if (xiaoli[k]) {
continue;
}
xiaoli[k] = laoli[k];
}}
封装一个对象之间继承的函数
function extend(parent, child) {
for (var k in parent) {
// 子级有的属性不需要继承
if (child[k]) {
continue;
}
child[k] = parent[k];
}
}
// 调用函数实现继承
extend(laoli,xiaoli);
console.log(xiaoli);
2.原型继承
提取两个对象所有公共的属性,然后放到一个父类型中
比如学生 和老师,公共的属性有 name age sex ,用构造函数创建一个人类
Person对象,书写两者公共的3条属性。
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(score) {
this.score = score;
}
// 老师类型
function Teacher(salary) {
this.salary = salary;
}
// 原型对象,可以将自己的属性和方法继承给将来的实例对象使用
Student.prototype = new Person("zs",18,"男");
Student.prototype.constructor = Student;
// 生成一个实例
var s1 = new Student(89);
var s2 = new Student(100);
console.dir(s1);
console.dir(s2);
console.log(s1.name);
console.log(s1.constructor);
- call方法
// call
// 函数本身就是一种对象,就能够有自己的属性和方法
// call 方法本身是一种执行函数的方法
function fn(a,b) {
console.log(this);
console.log(a + b);
}
// 自定义一个简单对象 o
var o = {
name: "zs"
}
// 普通函数调用
// fn(2,3);
// call 方法在调用函数的时候,有两个功能
// 1.更改函数内部的 this 指向,原先默认是 window
// 2.调用函数执行内部代码
// 参数: 第一个参数用来指定 this,第二个及以后,就是传的实参
fn.call(o,3,4);
4.属性的继承
// 构造函数的属性的继承
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(name,age,sex,score) {
// 直接对父类型的构造函数进行一个普通调用
// Person 普通调用过程中,内部的 this 指向的是 window
// 可以通过 call 方法更改Person 内部的 this,现在的this就指向了Student
Person.call(this,name,age,sex);
this.score = score;
}
// 老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
// 创建学生的实例对象
var s1 = new Student("zs",18,"男",89);
var s2 = new Student("ls",19,"男",92);
console.dir(s1);
console.dir(s2);
-
方法 的继承
// 父类型的原型对象中有方法也需要继承 Person.prototype.sayHi = function () { console.log("你好"); }; // 学生类型 function Student(name,age,sex,score) { Person.call(this,name,age,sex); this.score = score; } // 子类型的原型对象上,需要继承父类型原型对象的方法 // 方法1:对象拷贝继承 // for (var k in Person.prototype) { // // 保留自己的 constructor 不要进行继承 // if (k === "constructor") { // continue; // } // Student.prototype[k] = Person.prototype[k]; // } // 方法2:原型继承 Student.prototype = new Person(); Student.prototype.constructor = Student; // 老师类型 function Teacher(name,age,sex,salary) { Person.call(this,name,age,sex); this.salary = salary; } // 创建学生的实例对象 var s1 = new Student("zs",18,"男",89); var s2 = new Student("ls",19,"男",92); console.dir(s1); console.dir(s2); s1.sayHi();
6.组合继承,属性在构造函数中继承,方法通过原型继承,参照4 5的结合
// 组合继承:属性在构造函数内部继承,方法通过原型继承
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log("你好");
}
// 生成一个子类型
function Teacher(name,age,salary) {
// 继承父类的属性
Person.call(this,name,age);
this.salary = salary;
}
// 方法继承,通过原型对象继承
Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;
// 生成老师的一个实例
var t1 = new Teacher("wang",45,10000);
console.dir(t1);
console.log(t1.name);
t1.sayHi();
继承的原理,方法为什么可以被继承:
7.函数定义的方式
函数表达式可以没有名字,如匿名函数,函数定义和声明的区别:
fun(); 函数调用
// 函数声明
// 必须定义函数名
function fun() {
/ console.log(1);
}
// 函数表达式
// 是将函数赋值给一个变量,可以是一个匿名函数
var fn = function () {
console.log(2);
};
fn();
8.函数定义的new方式
函数本身也是一种对象,通过new Function 的方式来定义的,如:
var fun = new Function('a','b','var a = "1";console.log(a+b)');
fun(2,3);
console.dir(fun);
传的参数都 是字符串 ,要用单引号或者双引号,一般不推荐,还是用传统的函数声明调用方式
函数本身也是一种对象,因此可以调用属性和方法
9.函数的调用和this
普通函数内的this默认指向window;构造函数中this指向的是将来创建的实例对象;
对象中的方法,里面的对象指的是调用的对象自己;事件函数的内部 this 指向的是事件源,
如 btn ,document;定时器和延时器中的函数,默认内部的 this 指向的是 windowthis真正指向的是谁,要看其调用形式和执行环境
10.函数的 call apply bind 方法,都可以打点调用,如 fun.call( 参数) fun是函数名,括号
function fun(a,b,c,d) {
console.log(this);
console.log(a + b + c + d);
}
// call方法:1.功能:第一个可以指定函数的 this,第二个可以执行函数并传参
// 2.参数:第一个参数,传入一个指定让 this 指向的对象,第二个参数及以后,是函数参数的列表
// 3.返回值:就是函数自己的返回值
// 4.测试
var o = {
name: "zs"
}
// fun.call(o,1,2);
// apply 方法
// 1.功能:第一个可以指定函数的 this,第二个可以执行函数并传参
// 2.参数:第一个参数,传入一个指定让 this 指向的对象,第二个参数是函数的参数组成的数组
// 3.返回值:就是函数自己的返回值
// 4.测试
// fun.apply(o,[4,5]);
// bind 方法
// 1.功能:第一个可以指定函数的 this,bind 方法不能执行函数,但是可以传参
// 2.参数:第一个参数,传入一个指定让 this 指向的对象,第二个参数及以后,是函数参数的列表
// 3.返回值:返回一个新的指定了 this 的函数,也可以叫绑定函数
// 4.测试
var fn = fun.bind(o,2,3);
console.log(fn);
fn(6,7); 输出为2+3+6+7=18,前提是原函数fun传入4个参数才可以
- call方法的运用
如何让一个字面量书写的对象,像数组一样,直接调用 arr.push() 方法添加新的元素呢?
// {} 的对象自己是没有 push 方法的
// 类数组对象 getElementsByTagName
var o = {
0: 10,
1: 20,
2: 30,
length: 3
};
// console.log(o[0])
// 增加一项新的数据,原先老实方法
// o["3"] = 40;
// o.length = 4;
// 利用数组中的 push 方法,指定内部的this 为对象 o,就可以处理类数组对象的数据
Array.prototype.push.call(o,50);
console.log(o);
// 输出为:50添加到了o 对象的第四项上了
- apply方法的运用
内置在js中的方法比如Math的一些方法,如何将数组作为参数呢?
// 定义一个数组,利用 apply 方法,可以将它拆开进行操作
var arr = [1,3,4,6,8];
// 想借用一些现在内置在js 中的方法
// console.log(Math.max(1,3,5,7,9));
// 利用 apply 方法,将数组传给 max 的第二个参数
// console.log(Math.max.apply(Math,arr));
console.log(1,2,3);
console.log.apply(console,arr); ---输出为 1 3 4 6 8分开的数字而非数组
- bind方法的运用
// 想修改的是定时器的函数内部的 this
var o = {
name: "zs",
age: 18,
s: function () {
setInterval(function () {
console.log(this.age);
}.bind(this),1000);
}
}
// o.s();
// 更改 事件函数中的 this
document.onclick = function () {
console.log(this);
}.bind(o); 输出的是o这个对象,不执行定时器这个函数
-
函数的其他成员
console.dir一个函数,可以得到函数内部的成员
arguments:传入的是函数在调用 时,传入的所有实参组成的类数组对象,有一个callee属性,类似constructor,
caller 函数的调用者,函数在哪个作用域调用,caller就是谁,如果是在全局调用,caller就是null
如果是在内部执行的话,如:
function test() { fn(1,2,3,4); } test();
此时caller就是调用的函数名称 test
length:形参的个数
name:函数的名字
使用arguments对象,找到最大的一个实参,如:
function max() { // 判断实参中最大的数 var nowMax = arguments[0]; for (var i = 1 ; i < arguments.length;i++) { if (arguments[i] > nowMax) { nowMax = arguments[i]; } } return nowMax; } console.log(max(1,4,7,9));
-
高阶函数:函数可以做参数的函数;函数可以作为返回值的函数
1.函数作为另一个函数的参数时:
// 定义一个函数,吃饭的函数,吃完饭之后,可以做其他的事情,看电影、聊天、看书 function eat(fn) { console.log("吃晚饭"); // // 接下来的要做的事情是不固定的 fn(); } eat(function () { console.log("看电影"); }); 输出结果是 吃晚饭 看电影
2.函数作为另一个函数的返回值
需求:通过同一段代码实现以下效果 // 输出 100 + m // 输出 1000 + m // 输出 10000 + m function outer(n) { return function inner(m) { console.log(m + n); } } // 在外部执行 inner 函数 // 100 + m var fun = outer(100); fun(3); 输出为103. fun(13); 输出为113 fun(23); 输出为123 var fun1 = outer(1000); fun1(3); 输出为1000+3=1003
-
函数闭包:天生存在的,函数记住自己的作用域和函数自己,函数自己也
就是一个闭包,不论函数以何种方式进行调用,都会回到自己定义时的密闭
环境进行执行:即如果函数调用时跑到外部进行调用,也会执行原先在内部生
成的语句
体会一下闭包,把内部函数拿到外部父函数外面,看能不能调用父函数内部的变量:
```
// 将一个内部函数拿到父函数的外面,观察是否还能调用父函数内部的变量
function outer() {
var a = 10;
function inner() {
console.log(a);
}
// 将inner 函数作为返回值
return inner;
}
outer()
// 在outer函数的外面,是不能直接访问 a 变量,如下面两条语句就无法执行
// outer();
// console.log(a);
// 将 outer 执行的结果,赋值给一个变量
var inn = outer();
console.log(inn);
// 在全局调用 inn,按道理应该查找全局的 a变量
inn();
// 输出的真正结果是 10,来自于 outer 函数内部的变量
```
-
闭包功能:1.可以在函数外部读取函数内部成员
2.让函数在外部延长函数内部变量的存活时间,不会被马上消除,如:
// 将一个内部函数拿到父函数的外面,观察是否还能调用父函数内部的变量 function outer() { // 形成闭包环境中的变量不是一成不变的,可以被更改 var a = 10; function inner() { console.log(a++); } // 将inner 函数作为返回值 return inner; } var inn = outer(); inn(); 输出结果为10 inn(); 变量a未消失,且可以被改变,此时的a变成了11,所以输出为11
本身变量a在执行一次就在内存中被销毁了,因为闭包,所有这个变量可以被调用两次
闭包带来的问题:使用自调用函数解决
五、正则表达式
1.在线正则表达式训练网站
c.runoob.com/front-end/845
2.创建正则的方法
正则表达式也是对象,是一种索引类型 ,创建方法2种:推荐使用字面量方式
// 创建正则的第一种方法,正则的字面量 /
var reg = /abc/;
// 第二种,通过 构造函数 创建
var reg1 = new RegExp("cde");
3.相关正则方法
字符串方法与其对应的正则表达式:
4.正则表达式的组成
六、 ES6 新特性 习惯将ES2015称为ES6
1.解决了原有语法的一些问题或者缺陷;对原有语法进行增强;
全新的对象,全新的方法,全新的功能;全新的数据类型和数据结构
2.最新版本的浏览器,谷歌最新版,可以直接在浏览器中执行;可以在VS中安装相关插件
3.let和块级作用域
通过现代关键字let定义块内部的变量 其定义的变量在块级作用域内部可以被访问
非常适合设置 在for 循环中的循环变量:如:
// 非常适合设置 在 for 循环中的循环变量
for (var i = 0 ; i < 3 ; i++) {
for (var i = 0; i < 3;i++) {
console.log(i);
}
} 输出一次 0 1 2
因为for 里面的变量都是全局变量,变量间会发生覆盖,最终值执行了一次外层循环,三次内层循环
没有输出9个,输出 0 1 2
let设置:
// 通过 let 定义变量,只在自己的循环中生效
for (let i = 0 ; i < 3 ; i++) {
for (let i = 0; i < 3;i++) {
console.log(i);
}
}
此时就会输出3次 0 1 2
通过循环批量添加事件,之前的用法:
// 通过 let 定义变量,只能在块级内部被调用
var eles = [{}, {}, {}];
for (var i = 0 ; i < eles.length ; i++) {
eles[i].onclick = function () {
console.log(i);
}
}
eles[0].onclick();
最终结果,不管传入的实参是0 还是 1 2,结果都是3 ,因为for循环内部var 定义的是全局变量,循环开始后 全局变量 i先后为 0 1 2,最终 i的值会由最新的 2替换,再i++为3,最终输出一个 3
解决方法:将var 替换成let, 或者用一个自调用函数封闭i的作用域
循环:实际有两层作用域
for (var i = 0 ; i < 10 ; i++) {
var i = "foo";
console.log(i); 只输出了一次foo
for (var i = 0 ; i < 10 ; i++) {
let i = "foo";
console.log(i); 输出了10次foo ,for循环括号里的var也可以换成let
4.const
const name=“zs”;
name=“ls”;
此时name不会被更改为ls。const声明的时候必须同时赋予一个初始值
const obj={};
obj.name="zs";
给这个空对象添加属性和属性值是可以的,这个obj指向的对象是没有改变的
补充:主要用const,配合let,不用var
5.数组的解构
// 数组解构
const arr = [100, 200, 300]
const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)
新的方法:`
const arr = [100, 200, 300]`
const [foo, bar, baz] = arr`
console.log(foo, bar, baz)` 输出为100 200 300,对应赋值
如果只想要获取第三个成员,可以写成 const [, , baz] = arr
;
获取成员后的剩余成员:
const arr = [100, 200, 300]
const [foo, ...rest] = arr
console.log(rest)
数组的灵活运用:
const arr = [100, 200, 300]
const [foo, bar, baz = 400, more = 123] = arr
console.log(more) 输出为123
console.log(baz) 输出为400而不是300
将字符串分割为数组,用数组的解构,如:
const path = "foo/bar/baz"
// const temp = path.split("/") 之前的方法,获取下标为1的项 bar
// const a = temp[1]
const [,a,] = path.split("/")
console.log(a)
6.对象的解构,同数组的解构很像:
// 对象解构
const obj = { name: 'zs', age: 18 }
const { name } = obj
console.log(name) 获取name 的属性和值
const obj = { name: 'zs', age: 18 }
const name = "tom"
const { name: newName } = obj
console.log(name) 输出tom
console.log(newName) 输出为zs
7.模板字符串,加反引号`
添加反引号可以给字符串换行,
${ }插值表达式 可以将里面的内容插入到字符串中,如:
const name = "tom"
const str = `hey, ${name},${1 + 1},${Math.random()}`
console.log(str) 输出为hey,tom,2,一个随机数
8.模板字符串标签函数
const name = "zs"
const gender = true
function myTagFunc(strings, name, gender) {
// console.log(strings,name,gender)
// 处理一下 性别
const sex = gender ? "man" : "woman"
return strings[0] + name + strings[1] + sex + strings[2]
}
const str = myTagFunc`hi, ${name} is a ${gender}`
console.log(str)
9.字符串扩展方法
const msg = 'Error: foo is not defined.'
console.log(msg.startsWith('Error')) 判断是否以xx开头
console.log(msg.endsWith('.')) 判断是否以xx结尾
console.log(msg.includes('foo')) 判断是否包含xx
10.参数默认值
形参设置默认值,实参没有设置,传入的就是默认值
function foo(enable = true,bar) { 参数默认值在前
// enable = enable || true
// enable = enable === undefined ? true : enable
console.log('foo invoked enable:')
console.log(enable)
}
foo('bar') 输出为foo invoked enable: bar,因为此时默认值被实参‘bar’替代了
function foo(bar,enable = true) { 一般都是参数默认值在后
// enable = enable || true
// enable = enable === undefined ? true : enable
console.log('foo invoked enable:')
console.log(enable)
}
foo('bar') 输出为 foo invoked enable: true
9.剩余操作符
function fun(...args) {
console.log(args)
}
fun(1,2,3,4) 输出为所有实参的数组 [1,2,3,4]
function fun(n,...args) {
console.log(args)
}
fun(1,2,3,4) 输出为除第一个实参1的剩下实参的数组 [2,3,4]
11.展开操作符
// 展开数组操作
const arr = ['foo', 'bar', 'baz']
// console.log(arr[0],arr[1],arr[2]) 传统的死办法
// console.log.apply(console,arr) 利用apply 的方法,console对象的this指向的就是console自己
console.log(...arr) es2015的方法,输出为[foo,bar,baz]
12.箭头函数 =>
将下面这个函数用箭头函数改写:
function plus(a) {
return a + 1
}
console.log(plus(10))
改写为:
const plus = (a, b) => { 只要一个参数可以不用加(),如果箭头后只有
console.log('plus invoked') 一条语句,那这条语句就是返回值
return a + b
}
console.log(plus(1,2)) 输出为3
const arr = [1,2,3,4,5,6,7]
// const arr1 = arr.filter(function (item) { 筛选数组中的奇数,filter筛选函数
// return item % 2 只有奇数才会返回1,也就是true
// })
const arr1 = arr.filter(i => i % 2) 用箭头函数简化上面的筛选函数
console.log(arr1)
12.箭头函数的this 内部没有this 的机制,从外部进行查找
// 箭头函数与 this
const person = {
name: "tom",
// sayHi: function () {
// console.log(`hi,my name is ${this.name}`) 这里的this指向的是person对象自己
// }
// sayHi: () => {
// console.log(`hi,my name is ${this.name}`) 箭头函数这里的this找不到指向的内容
// }
sayHi: function () {
setTimeout(() => { 箭头函数省去了函数名
console.log(`hi,my name is ${this.name}`) 箭头函数本身没有this,从外部找到了this,指向的是person,如果没有用箭头函数,则无法正确执行
},1000);
}
}
person.sayHi() 可以正确执行
13.对象字面量的增强
// 对象字面量增强
const bar = "bar"
const age = "age"
const obj = {
name: "tom",
// bar: bar
bar, 属性名和属性值一样可以省去属性值
sayHi () { 方法可以不用写函数名
console.log('hi')
console.log(this) this指向的是对象obj
},
// 计算属性名
[1+2]: 18 添加表达式作为属性名,[]里面可以是表达式也可以是变量
}
// obj[age] = 18 以前的方法。obj添加[]进行更改。[]内部是动态的属性名,乐意是表达式
console.log(obj)
// obj.sayHi()
14.对象扩展的方法 Object.assign方法
const source1 = {
a: 123,
b: 123
}
const source2 = {
b: 678,
d: 789
}
const target = {
a:456,
c:789
}
const result = Object.assign(target,source1,source2) 将后面两个对象的属性合并到target中去,同样的属性会采用后面对象的属性值进行覆盖
console.log(target)
console.log(target === result) 最终的新对象还是target对象
// 应用,在 options 对象参数接收时,简化
function Block(options) {
// this.width = options.width; 以前的方法,通过传入的实参添加Blcck的属性
Object.assign(this,options)
}
const block1 = new Block({width: 100, height: 100, x: 50, y: 50})
console.log(block1)
15.Object.is方法 :判断两个是否是同一类型的数据
Object.is(NaN,NaN)
值为false
16.class
function Person(name, age) { 之前构造函数的方法
// this.name = name;
// this.age = age;
// }
// Person.prototype.sayHi = function () {
// console.log(`hi,my name is ${this.name}`)
// }
class Person { class的方法,也更加整洁
constructor (name, age) { 参数在constractor后书写
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
}
const p1 = new Person("tom",18) 同样的生成p
console.log(p1)
p1.sayHi() 实例对象调用方法
17.静态方法 static
使用static修饰的属性和方法,可以直接由对象进行调用,生成的对象实例反而不能调用成功
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
static create (name,age) {
console.log(this) this指向的是不是实例对象,而是Person
return new Person(name,age)
}
}
const p1 = Person.create("zs",19) 调用时打点是crrate 不是static
console.log(p1)
- 类的继承 使用extends
// 静态方法
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
}
class Student extends Person {
constructor (name,age,number) {
super(name,age) super对象指向的是父类
this.number = number;
}
hello () { 给Student添加自己的一个方法
super.sayHi() 使用的是父类的方法,用super打点调用父类的方法
console.log(`学号是 ${this.number}`) 自己的方法的表达式
}
}
const s1 = new Student("tom",18,102)
s1.hello();
-
set 的全新结构,Set是一个对象
const s = new Set() 生成 s 这一个实例对象 s.add(1).add(2).add(3).add(4).add(2) 重复的内容会被忽略 console.log(s)
s.forEach(i => console.log(i))
ES5中的遍历方法 for each`for (let i of s) {` ES2015的遍历方法 for of `console.log(i)` `}`
console.log(s.size)
可以得到集合的长度判断集合中是否存在某个值
console.log(s.has(4))
console.log(s.delete(100)) 判断是否删除成功 console.log(s) 输出为true或者flase
s.clear()
删除集合中所有数据set一般用作数组去重:
// 数组去重 const arr = [1.3,4,6,2,4,7,5,8] // const b = Array.from(new Set(arr)) 方法一:把集合转换为数组 Array.from方法 const b = [...new Set(arr)] 方法2: ...rest方法 console.log(b)21
-
Map数据结构 const obj = {} obj[true] = "boolean" obj[123] = "number" obj[{a: 1}] = "object" 对象型的数据,此时无法转换成字符串的键值对集合 console.log(Object.keys(obj)) key可以让对象中的属性和属性值转换字符串的键值对集合 console.log(obj[{}]) console.log(obj['[object Object]'])
使用Map让对象类型也可以转换为字符串的键值对集合:
const map = new Map() const a = { a: 1} map.set(a,100) map 的set方法,a参数指代对象类型的属性,100是对象类型的属性的属性值 console.log(map) console.log(map.get(a))
map 也有 has delete clear方法
-
symbol 本身表示独一无二的值,直接以函数方式执行 Symbol()
`const s = Symbol()` `console.log(s)` `console.log(typeof s)` 输出为symbol
Symbol() 函数内部可以传入参数,参数就是对这个数据的描述,用作区分
如:Symbol(” ddd“ )
Symbol()`可以作为对象的属性,
const obj = { [Symbol()] : 789, name: "zs" } obj[Symbol()] = 123 obj[Symbol()] = 456 console.log(obj[Symbol()])无法在外部进行访问
symbol最主要的作用就是为对象添加独一无二的属性
-
for of遍历
之前的变量 for each for in
const arr = [100, 200, 300, 400] for (const item of arr) { console.log(item) } arr.forEach(item => { 没有办法打断遍历 console.log(item) }) for (const item of arr) { console.log(item) if (item >= 200) { break; } }输出为100 200 const s = new Set(["foo", "bar", "baz"]) for (const item of s) { console.log(item) } const m = new Map() m.set("foo",1) m.set("bar",2) for (const [key,value] of m) { console.log(key,value) 输出为foo和1;bar和2的两个键值对 } const obj = { name: "zs", age: 18 } for (const item of obj) { 不用于对象的遍历 console.log(item) }
-
ES 2016新增内容
```
// ES2016 新增内容
const arr = [1,true,NaN,23,'hello']
// console.log(arr.indexOf(true))存在会返回1
// console.log(arr.indexOf(null))不存在会返回-1
// console.log(arr.indexOf(NaN))无法查找NaN是否存在
// includes 包含
// console.log(arr.includes(NaN))包含返回true
// 指数运算符 **
// console.log(Math.pow(2,3))自带的方法,下面ES2016的方法更便捷
console.log(2 ** 10)
```