🧙‍♂️闭包应用场景之--面向对象私有属性和方法的封装

94 阅读4分钟

前言

前面写过关于JavaScriptthis指向的基础文章,链接:juejin.cn/post/751796…

今天我们继续来讲讲闭包的应用场景之一:实现私有属性和方法的封装

实例

关于私有变量和公有变量的区分

私有变量直接声明,只能由函数内部的其他函数访问,这得益于闭包的特性。

公有变量通过this暴露出去,外部可以直接访问或调用。

写法是否可被外部访问
function xxx(){} 定义在函数体内,未暴露私有,外部不可访问
挂载到 this.xxx = xxx 或返回对象中公有,外部可访问
 function Book(title,author,year){
    let _title = title; // _约定表示私有变量 内部可以使用 有利于可读性的编程风格
    let _author = author;
    let _year = year;
}

声明一个构造函数,这三个属性是共有属性,用_开头表示私有变量,有利于可读性的编程风格。

私有属性只有内部才可以访问

我们实例化一个对象book,试图访问私有变量 _title

let book = new Book('js高级程序设计','张三',2020)
console.log("1...."+book._title); // 私有属性 外部无法访问 undefined

结果输出undefined,证明私有变量只能在函数内部访问,在外部不可以访问

image.png 在函数中加一个公有属性方法:

 function Book(title,author,year){
    let _title = title;
    let _author = author;
    let _year = year;
    this.getTitle = function(){ // 对外提供方法 可以访问私有变量 闭包
        return _title;
    }
}

这里的 this.getTitle是公有属性,赋了一个匿名函数给它,是一个公有方法。

在这里可以访问到私有属性_title

 console.log("2...."+book.getTitle());// 可以访问

image.png

这里实现是靠闭包实现的,使得外部的book对象能够访问到内部的私有属性。

我们继续添加一个私有方法 getFullTitle

 function Book(title,author,year){
    let _title = title;
    let _author = author;
    let _year = year;
    this.getTitle = function(){ // 对外提供方法 可以访问私有变量 闭包
        return _title;
    }
    function getFullTitle(){
        return `${_title} by ${_author}`// 私有方法 外部无法访问
    }
}
 console.log("4...."+book.getFullTitle);// 私有属性(方法) 外部无法访问 undefined

image.png

一样的道理,外部不可以访问私有变量。

假设我们这样访问它:

 console.log("4...."+book.getFullTitle());

结果:

image.png 这里说book.getFullTitle不是一个函数,这是为何??

  • getFullTitle 是一个私有方法,只能在 Book 构造函数内部访问;
  • 它没有被绑定到 this 上,也没有通过返回值暴露出去。
  • 而尝试通过 book.getFullTitle() 来访问它 —— 这其实是试图访问对象上的一个公共方法
  • 所以这写法本身是“访问公有方法”的方式;
  • 想访问的是一个“私有方法”,当然会失败。

我们继续添加公有方法:

function Book(title,author,year){
    let _title = title; 
    let _author = author;
    let _year = year;
    this.getTitle = function(){ 
        return _title;
    }
    this.getAuthor = function(){ // 闭包
        return _author;
    }
    this.getYear = function(){ // 闭包
        return _year;
    }
    function getFullTitle(){
        return `${_title} by ${_author}`
    }
    this.getFullInfo = function(){
        return `${getFullTitle()} published in ${_year}`
    }
   this.updateYear = function(newYear){ 
        if(typeof newYear === 'number' && newYear > 0){
            _year = newYear;
        }else{
            console.log('Invalid year');
        }
    }
}
console.log("5...."+book.updateYear(2021));
// 可以访问,但没有返回值
console.log("6...."+book.updateYear("123"));
// 传入的格式错误,而打印要返回某值,
//没有显式地返回任何值,因此它的默认返回值是 undefined。 

image.png 这里的book.updateYear(2021)是公有方法,可以访问,但是它并没有显式地返回任何值,故为undefined

book.updateYear("123")因为传入的值不符合if语句,进入else语句,打印出Invalid year。而执行完毕后,要控制台打印,同上,没有显式返回任何值,故打印undefined

如:

 book.updateYear("123") // 输出Invalid year

image.png

而前面第五个打印语句:console.log("5...."+book.updateYear(2021));通过闭包修改了_year的值:

console.log("7...."+book.getYear());// 2021

image.png

结尾

总的来说,闭包不仅为JavaScript带来了灵活的作用域控制能力,更为面向对象编程中的封装思想提供了实践路径。通过合理利用闭包特性,我们能够更优雅地实现私有属性和方法的隐藏与暴露,提升代码质量和安全性。掌握闭包的应用,是深入理解JavaScript语言特性的关键一步,也是编写高质量、可维护代码的重要基础。希望本文能帮助你更好地理解和运用闭包这一实用特性!