Javascript老生常談之面向對象

背景

做爲一個前端新人,免不了加各類羣,和其餘小夥伴們一塊兒學習(chui bi),互相幫助(bi can)。前幾天一個小夥伴在羣裏發了道本身去面試的筆試題,我寫了一下,一時間沒能實現,今天又折騰了一下,雖然大體實現了,但不知道是否優雅,分享本身解法的同時也想請大佬指點一二。javascript

題目

編寫一個名字爲Person的對象,要求

  • 有一個name屬性,外部只能訪問不能修改,初始化時賦值。前端

  • 內部維護一個叫things的局部變量,數組類型,用於存儲購買的物品(something)清單。java

  • 有一個buy(something)方法,用於購買物品(something)面試

  • 具備count屬性,用於指示一共買了多少物品。數組

分析

看到這題的第一反應就是用構造函數/class來寫。Person對象應該就是一個Person類。
兩個屬性一個方法,嗯,沒什麼問題。只有2個點須要考慮:閉包

1、 這個things局部變量是個啥?是屬性嗎?函數

  • 應該不是,既然特地指出了,確定有他的考慮,那麼既然是局部變量,外部也是訪問不到的,這個應該用閉包寫。學習

2、 name屬性外部只能訪問不能修改.ui

  • 這個用閉包也能解決,可是這樣記不能經過構造函數/class來實現了,背離了初衷,不行(不過我後面仍是會給出這種寫法)。那麼(以個人水平)就只能用Proxy或者class,這2個都能攔截對象屬性的讀/寫。2種我都試過,採用Proxy,緣由下面會講。this

代碼

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種方法的優勢就結合到一塊兒了Σ(っ °Д °;)っ
這2小時沒有白花,這文章沒有白寫,又賺到了^ ^。
但願能給你們帶來一點點收穫,若是有不一樣的見解能夠留言一塊兒探討()。

Thanks for reading

相關文章
相關標籤/搜索