背景
作为一个前端新人,免不了加各种群,和其他小伙伴们一起学习(chui bi),互相帮助(bi can)。前几天一个小伙伴在群里发了道自己去面试的笔试题,我写了一下,一时间没能实现,今天又折腾了一下,虽然大致实现了,但不知道是否优雅,分享自己解法的同时也想请大佬指点一二。
题目
编写一个名字为Person的对象,要求
有一个name属性,外部只能访问不能修改,初始化时赋值。
内部维护一个叫things的局部变量,数组类型,用于存储购买的物品(something)清单。
有一个buy(something)方法,用于购买物品(something)
具有count属性,用于指示一共买了多少物品。
分析
看到这题的第一反应就是用构造函数/class来写。Person对象应该就是一个Person类。
两个属性一个方法,嗯,没什么问题。只有2个点需要考虑:一、 这个things局部变量是个啥?是属性吗?
应该不是,既然特意指出了,肯定有他的考虑,那么既然是局部变量,外部也是访问不到的,这个应该用闭包写。
二、 name属性外部只能访问不能修改.
这个用闭包也能解决,但是这样记不能通过构造函数/class来实现了,背离了初衷,不行(不过我后面还是会给出这种写法)。那么(以我的水平)就只能用
Proxy
或者class
,这2个都能拦截对象属性的读/写。2种我都试过,采用Proxy,原因下面会讲。
代码
class Person { constructor(name,count=0) { this.name = name; this.count = count; this.init() } //初始化函数 init() { var things = []; Person.prototype.buy = (something) => { //为了形成闭包,显式将buy方法写到Person的原型上 things.push(something); this.count = things.length; } }}//Proxy handler对象,定义行为var handler = { set(target,prop,value,receiver) {//拦截set行为 if(prop == 'name') { throw Error('不可以哦') //这里自定义你的逻辑,也可以alert等。 } Reflect.set(target,prop,value,receiver) //不要忘了对其余属性'放行' }}var p1 = new Proxy(new Person('张三'),handler)console.log(p1.name) //张三p1.name = '李四' //Error:不可以哦p1.buy('猫粮');p1.buy('猫砂');console.log(p1.count) //2//如果需要从'内部'修改p1的名字,则需要先对被代理对象进行定义var _p1 = new Person('张三');var p1 = new Proxy(_p1,handler)console.log(p1.name); //张三_p1.name = '李四';console.log(p1.name) //李四
以上我对这道题的理解。至于为什么不用class的setter,是因为这样写后,在new Person的时候不能传name进去,因为一传进去就会被拦截。所以只能先初始化对name赋值,再进行proxy代理对name拦截。
另一种“野路子”写法
var Person = function(name,count=0) { var things = [],name = name result = { count = count; }; result.__proto__ = { buy:function(something) { things.push(something); result.count = things.length; }, getName:function() { // 通过getName函数来获取名字 return name; } } return result;}var p2 = Person('王五'); p2.getName() //王五p2.name = 123;p2.getName() //王五p2.buy('妙鲜包');console.log(p2.count) // 1
两种方法见仁见智,第二种野路子不用Proxy代理,返回的是Object对象,而第一种正规军则返回Person对象,更符合题意。
结语
写到这不知道大家发现了没。 其实第一种方法也可以不需要proxy代理,再init
函数中定义name = this.name,再在Person原型上写一个getName
函数,这样2种方法的优点就结合到一起了Σ(っ °Д °;)っ
Thanks for reading