proxy
代理,可以帮我们完成对数据的处理与验证之类的事情,访问对象前添加一层拦截。基本语法let p = new Proxy(target, handler),第一个参数是要用Proxy包装的目标对象(可以使任意类型的对象),第二个参数是一个对象,其属性就是当执行一个操作时定义的代理行为函数。说白了proxy就相当于一个明星的经纪人,我们一般直接联系不到明星吧,我们想要请明星出场就得通过经纪人,下面我们来举例说明。
let rapStar = { name: 'jiangyunsheng', age: 24, phone: 19888888888 } let agent = new Proxy(rapStar, { // 取值函数,可以拦截到读取操作 get (target, key) { if(key === 'phone'){ // 我们一般人是获取不到明星的手机号吧,要联系也是通过经纪人 return `agent: 1383838438`; } if(key === 'price'){ // rapStar上并没有这个属性,搞艺术的本身是无价的,但是总归得有出场费吧 return 300000; } return target[key]; // 其余不敏感的信息,我们都可以直接获取到 }, // 存值函数,可以拦截到赋值操作 set (target, key, value) { // 经纪人报价,我们要讨价还价的嘛,但也不能太低对不对,如果价格合适,皆大欢喜 if(value < 200000){ throw new Error('太低了吧'); }else{ target[key] = value; return true; } }, // 可以拦截到 in 操作符,但是无法拦截 for in 循环的 has (targer, key) { console.log('详情请联系经纪人: 1383838438') if (key === 'customerPrice'){ return true; }else{ return false } } }) console.log(); // -> jiangyunsheng console.log(agent.phone); // -> agent: 1383838438 console.log(agent.price); // -> 300000 agent.customerPrice = 250000; // -> 这里我们报价 250000 console.log(agent.customerPrice); // -> 250000 接受了我们的报价 // 看看有没有客户报价 console.log('customerPrice' in agent); // -> true 并打印 详情请联系经纪人: 1383838438复制代码
这只是最基础的应用,其他的大家可以自行摸索,都是一样的用法。
class
javascript中只有对象,没有类的概念,是基于原型的面向对象语言。原型对象特点就是讲自身的属性共享给实例化出来的新对象。如果我们要生成一个实例对象,就先需要定义一个构造函数,然后通过new操作符来完成实例。
function Person (name, age) { = name; this.age = age; } Person.prototype.say = function () { return 'My name is ' + ; } var p = new Person('zhangsan', 22); console.log(p); // -> 下图 p.say(); // -> My name is zhangsan 复制代码
这里我们再简单回顾一下构造函数生成实例的过程,当new构造函数时,会隐式的创建一个对象,并把构造函数的作用域给新对象(this -> 新对象),然后执行构造函数内的代码,如果无返回值默认返回新创建出来的对象。
进入正题,ES6引入了class(类)这个概念,实际上就是构造函数的语法糖,写法上注意方法之间不要加逗号,会报错的。
class Person { constructor (name = 'zhangsan', age = 22) { // 实例化的属性配置:私有属性 = name; this.age = age; } // 公有方法 -> 原型上的方法 say () { console.log(`My name is ${}, ${this.age} years old.`) } } console.log(new Person); // -> 下图 let p = new Person; p.say(); // -> My name is zhangsan, 22 years old.复制代码
二者之间的小区别,类原型上的方法不可枚举。
function RapStar1 (name) { = name; } RapStar1.prototype.sing = function () { console.log(`${}: 遍体鳞伤的人绝不会倒在最艰苦的长征`); } // 这样也可以在原型上新增方法 Object.assign(RapStar1.prototype, { drink () { console.log('一箱啤酒') } }) console.log(Object.keys(RapStar1.prototype)); // -> ["sing", "drink"] class RapStar2 { constructor (name) { = name; } sing () { console.log(`${}: I just called to tell you drive safe`); } drink () { console.log('whisky'); } } console.log(Object.keys(RapStar2.prototype)); // -> []复制代码
当我们没有定义constructor的时候,并不会报错而是会默认为我们定义好一个,只能通过 new 的方式执行。
class Person { } console.log(new Person); Person(); // -> TypeError: Class constructor Person cannot be invoked without 'new'复制代码
class不存在变量提升(TDZ),只能先定义再使用,构造函数可以先使用,在定义。
new C(); // -> ReferenceError: C is not defined class C { } new B(); // -> B {} function B () { }复制代码
static静态属性,在构造器上新增属性/方法(有兼容性问题,静态方法可以,静态属性低版本会报错),class内部默认启用严格模式。
class Person { static a = 3; static say () { console.log('hhh'); } } console.log(Person.a); // -> 3 console.log(Person.say()); // -> hhh复制代码
通过extends关键字实现继承,子类必须在constructor方法中调用super方法,否则新建实例时会报错,这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。
class Father { constructor (name= 'foo', money = 100) { = name; this.money = money; } say () { console.log('hello'); } } class Son extends Father { constructor (name = 'bar') { super(name); } } console.log(new Father()); console.log(new Son());复制代码
super 在对象中使用,可以指代对象的原型。
let obj = { a: 1, say () { console.log(super.b); } } let proto = { b: 2, c: 3 } Object.setPrototypeOf(obj, proto); obj.say(); // -> 2 复制代码
总结
- class 与 let、const 一样存在TDZ(暂时性死区)
- 公有属性不可枚举
- 内部默认严格模式
- 会默认生成constructor
- 只能通过new执行