目录
数组方法concat
- 拼接两个数组,返回一个拼接好的数组
Math.max()
- 获取最小值和最大值
- 如果参数为数组,可以用apply解决
得到一个函数的形参个数
- 函数名.length
ES6柯里化
ES6关于构造函数调用的问题
- 我们写一个构造函数,别人有可能不知道它是普通的函数还是构造函数,那么在调用这个函数的时候就会出现问题,构造函数需要通过new加构造函数来调用,如果使用普通的调用方式就会出现问题。
- 这个问题发生在this的指向上面,通过new来调用this指向的是构造函数本身这个是没有问题的;那么是普通的调用this的指向就是全局对象(在浏览器的环境下就是window)。
function Person(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${
firstName}${
lastName}`;
}
const p1 = new Person("张","三");
console.log(p1);
const p2 = Person("李","四");
console.log(p2);
- 在ES6之前我们是通过判构造函数里面的this的原型链上是不是有构造函数,如果有,那么就是通过new一个函数来调用的,代码如下:
function Person(firstName, lastName){
//判断this的原型链上有无Person
if(!(this instanceof Person)){
throw new Error("该函数没有使用new来调用")
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${
firstName}${
lastName}`;
}
const p1 = new Person("张","三");
// console.log(p1);
const p2 = Person("李","四");
// console.log(p2);
- 运行结果
- 但是通过上面这种方法还是有问题,问题就是我们通过call,apply,bind,改变this的指向,让this指向一个new Person,就可以绕过我们的判断。代码如下
function Person(firstName, lastName){
if(!(this instanceof Person)){
throw new Error("该函数没有使用new来调用")
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${
firstName}${
lastName}`;
}
const p1 = new Person("张","三");
console.log(p1);
const p2 = Person.call(p1,"王","二");
console.log(p2);
const p3 = Person("李","四");
console.log(p3);
- ES6解决了这个问题,new.target,该表达式,得到的是:如果没有使用new来调用函数,则返回undefined;如果使用new调用函数,则得到的是new关键字后面的函数本身
function Person(firstName, lastName){
// if(!(this instanceof Person)){
// throw new Error("该函数没有使用new来调用")
// }
if(new.target === undefined){
throw new Error("该函数没有使用new来调用");
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${
firstName}${
lastName}`;
}
const p1 = new Person("张","三");
console.log(p1);
const p2 = Person.call(p1,"王","二");
console.log(p2);
this的指向
- 通过对象调用函数,this指向对象
- 直接调用函数,this指向全局对象
- 如果通过new调用函数,this指向新创建的对象
- 如果通过apply、call、bind调用函数,this指向指定的数据
- 如果是DOM事件函数,this指向事件源
- this在函数里面的指向在注意!!!
const obj = {
count: 0,
start: function(){
// this->obj
console.log(this);
setInterval(function(){
//this -> window
this.count++;
console.log(this.count);
},1000);
},
bindEvent: function(){
// this->obj
window.onclick = function(){
console.log(this.count);
}
}
}
obj.start();
obj.bindEvent();
- 之前解决办法,保存this
const obj = {
count: 0,
start: function(){
// this->obj
const self = this;
console.log(this);
setInterval(function(){
//this -> window
self.count++;
console.log(self.count);
},1000);
},
bindEvent: function(){
// this->obj
const self = this;
window.onclick = function(){
self.count++;
console.log(self.count);
}
}
}
obj.start();
obj.bindEvent();
- ES6通过箭头函数解决这个问题
箭头函数
- 箭头函数是一个函数表达式(xxx=xxx),理论上, 格式
(参数1,参数2,...)=>{
//函数体
}
- 只有一个参数,小括号可以省略,函数体只有一条语句大括号可以省略,返回值只有一条语句return可以省略
//判读一个数是不是奇数
const isOdd = sum => sum % 2 == 0;
console.log(isOdd(2));//true
- 返回一个对象的时候要将对象用小括号包起来,不然会将其解析为一个函数体
//返回一个对象
const obj = (a,b)=>({
a:a,
b:b,
sum:a+b
});
console.log(obj(1,2));
- 箭头函数中,不存在this、arguments、new.target,如果使用了,则使用的是函数外层的对应的this、arguments、new.target
- 来个例子
const func = () => {
console.log(this);
}
func();//window
- 再来一个
const func = () => {
console.log(this);
}
const obj = {
method: func
}
obj.method();//window
- 实例
const obj = {
count: 0,
start: function(){
setInterval(() => {
this.count++;
console.log(this.count);
},1000);
},
bindEvent: function(){
window.onclick = () => {
this.count++;
console.log(this.count);
}
}
}
obj.start();
obj.bindEvent();
- 面试题
const obj = {
method: function(){
console.log(this);
const func = () => {
console.log(this);
console.log(arguments);
}
func();
}
}
obj.method(234);
- 可以将箭头函数替换为this来理解
- 箭头函数没有原型,就不能作为构造函数
const obj = (a) => {
console.log(a);
}
console.dir(obj);
console.log(obj,obj.prototype);
- 应用场景
- 临时性使用的函数,并不会可以调用它,比如:
- 事件处理函数
- 异步处理函数
- 其他临时性的函数
- 为了绑定外层this的函数
- 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
- 对象属性方法不要用箭头函数
//filter过滤,返回一个满足条件的数组
//map映射,把一个数组映射成一个新的数组,然后返回
//reduce累加器,返回一个结果
const arr = [1,2,3,4,5,6];
const arr1 = arr.filter(function(num){
return num%2 == 0;
})
const arr2 = arr1.map(function(num){
return num * 2;
})
const arr3 = arr2.reduce(function(total,a){
return total+a;
})
console.log(arr1);
console.log(arr2);
console.log(arr3);
- 箭头函数改写
const arr = [1,2,3,4,5,6];
const re = arr.filter( num => num%2 == 0).map( num => num*2).reduce((total,ele) => total+ele);
console.log(re);//24
直接赋值的变量会给window
新增的对象字面量语法
成员速写
- 如果对象字面量初始化时,成员的名称来自于一个变量,并且和变量的名称相同,则可以进行简写
function createUser(loginId,loginPwd,loginName){
const sayHello = function () {
console.log(this.loginName,this.loginId);
}
return {
// loginId: loginId,
// loginPwd: loginPwd,
// loginName: loginName,
loginPwd,
loginName,
loginId,
id: Math.random(),
sayHello
}
}
const p = createUser("123","adfs","张三");
p.sayHello();
console.log(createUser("123","adfs","张三"));
方法速写
对象字面初始化时,方法可以省略冒号和function关键字
function createUser(loginId,loginPwd,loginName){
return {
// loginId: loginId,
// loginPwd: loginPwd,
// loginName: loginName,
loginPwd,
loginName,
loginId,
id: Math.random(),
// sayHello: function(){
// console.log(this.loginName,this.loginId);
// }
sayHello(){
console.log(this.loginName,this.loginId);
}
}
}
const p = createUser("123","adfs","张三");
p.sayHello();
console.log(createUser("123","adfs","张三"));
计算属性名
有的时候,初始化对象时,某些属性名可能来自于某个表达式的值,在ES6,可以使用中括号来表示该属性名是通过计算得到的。
const prop1 = "name1";
const prop2 = "age1";
const prop3 = "sayHello1";
const obj = {
[prop1]: "老毕",
[prop2]: 23,
[prop3]: function(){
console.log(this[prop1],this[prop2]);
}
}
console.log(obj);
Object的新增API
Object.is(data1,data2)
- Object是构造函数
- 用于判断两个数据是否相等,基本上跟严格相等(===)是一致的,除了以下两点:
console.log(NaN === NaN);//false
console.log(-0 === +0);//true
console.log(Object.is(NaN,NaN))//true
console.log(Object.is(-0,+0))//false
Object.assign(target,sourse)
- assign有分配的意思
- 混合对象
const obj1 = {
a: "111",
b: "222"
}
const obj2 ={
a: "333",
d: "555"
}
//Object.assign(target,source),return target
//const obj = Object.assign(obj1,obj2);
//console.log(obj,obj1);
const obj = Object.assign({
},obj1,obj2);
console.log(obj,obj1);
const obj3 = {
...obj1,
...obj2
}
console.log(obj3);
Object.getOwnPropertyNames(obj)
- Object.getOwnPropertyNames方法之前就存在,只不过,官方没有明确要求,对属性的顺序如何排序,如何排序,完全由浏览器厂商决定。
ES6规定了该方法返回的数组的排序方式如下:
- 先排数字,并按照升序排序
- 再排其他,按照书写顺序排序
const obj = {
d: "123",
a: "234",
f: "as",
3: "123",
7: "123",
1: "234",
0: "12",
"-21": "12",
//有一样的属性名会看后面的值
"-10": "10",
"-10": "21",
"-2": "1",
"-1": "12"
}
const arr = Object.getOwnPropertyNames(obj);
console.log(obj);
console.log(arr);
Object.setPrototypeOf(obj1,obj2)
该函数用于设置某个对象的隐式原型
比如: Object.setPrototypeOf(obj1, obj2),
相当于: obj1.__proto__ = obj2
const obj1 = {
a: "1"
}
const obj2 = {
b: "2"
}
Object.setPrototypeOf(obj1,obj2);
面向对象简介
面向对象:一种编程思想,跟具体的语言
对比面向过程:
- 面向过程:思考的切入点是功能的步骤
- 面向对象:思考的切入点是对象的划分
【大象装冰箱】
类
- 传统的构造函数的问题
- 属性和原型方法定义分离,降低了可读性
- 原型成员可以被枚举
- 默认情况下,构造函数仍然可以被当作普通函数使用
类的特点
- 类声明不会被提升,与 let 和 const 一样,存在暂时性死区
- 类中的所有代码均在严格模式下执行
- 类的所有方法都是不可枚举的
- 类的所有方法都无法被当作构造函数使用
- 类的构造器必须使用 new 来调用
function Animal(name,sex,age,type){
this.name = name;
this.sex = sex;
this.age = age;
this.type = type;
}
Animal.prototype.print = function () {
console.log(`姓名:${
this.name} 性别:${
this.sex} 年龄:${
this.age} 种类:${
this.type}`)
}
const a = new Animal("dog","male","8","哈士奇")
a.print()
for (const key in a) {
console.log(key);
}
console.log(a);
console.log("-------------------------")
class Animal1 {
constructor(name,sex,age,type){
this.name = name;
this.age = age;
this.sex = sex;
this.type = type;
}
print(){
console.log(`姓名:${
this.name} 性别:${
this.sex} 年龄:${
this.age} 种类:${
this.type}`)
}
}
const aa = new Animal1("pig","famela",3,"花菇凉");
aa.print();
for (const key in aa) {
console.log(key);
}
console.log(aa);
类的其他书写方式
- 可计算的成员名
const prop = "print";
class Animal {
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
[prop](){
console.log(`${
this.name} ${
this.age} ${
this.sex}`);
}
}
const a = new Animal("a","12","male");
a[prop]();//a 12 male
getter和setter
- Object.defineProperty(this,“age”,{}) 可定义某个对象成员属性的读取和设置使用getter和setter控制的属性,不在原型上
const printName = "print";
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
//创建一个age属性,并给它加上getter,读取该属性时,会运行该函数
get age() {
return this._age + "岁";
}
//创建一个age属性,并给它加上setter,给该属性赋值时,会运行该函数
set age(age) {
if (typeof age !== "number") {
throw new TypeError("age property must be a number");
}
if (age < 0) {
age = 0;
}
else if (age > 1000) {
age = 1000;
}
this._age = age;
}
[printName]() {
console.log(`【种类】:${
this.type}`);
console.log(`【名字】:${
this.name}`);
console.log(`【年龄】:${
this.age}`);
console.log(`【性别】:${
this.sex}`);
}
}
var a = new Animal("狗", "旺财", 3, "男");
console.log(a);
Object.defineProperty
静态成员
构造函数本身的成员
使用static关键字定义的成员即静态成员
class Chess{
static width = 50;
static height = 40;
static method(){
//...
console.log(this.width+this.height);
}
}
console.log(Chess.width,Chess.height);//50 40
Chess.method();//90
字段初始化器(ES7)
注意:
1). 使用static的字段初始化器,添加的是静态成员
2). 没有使用static的字段初始化器,添加的成员位于对象上
3). 箭头函数在字段初始化器位置上,指向当前对象
class A{
static a = 1;
b = 2;
c = 3;
constructor(){
this.d = this.b + this.c;
}
}
const a = new A();
console.log(a);
class Animal{
a = 1;
//相当于
//constructor(a){
//this.a = a;
//}
}
- this指向问题
- 解决this指向问题
class Chess{
constructor(){
this.a = 123;
}
print = () => {
console.log(this.a);
}
}
const a = new Chess();
const p = a.print;
p();//123
- 因为下面这段代码为字段初始化,它就相当于在constructor中写,然后this的指向就被固定了,因为我们知道箭头函数没有this,它的this是函数体外面的
print = () => {
console.log(this.a);
}
constructor(){
this.print = () => {
console.log(this.a);
}
this.a = 123;
}
类表达式
const A = class{
a = 1;
b = 2;
}
const a = new A();
console.log(a);
实例成员和静态成员图解