文章目录 js 常用扩展方法总结 扩展方法应用选择 大型项目 中扩展方法应用选择 小型项目中 扩展应用
js 常用扩展方法总结
函数原型(prototype
)扩展方法 介绍 :在JavaScript中,通过修改函数的prototype
属性可以为该函数创建的所有对象添加共享的方法。这是一种基于原型链的扩展方式,所有通过该构造函数创建的对象都可以访问这些扩展的方法。示例 :
function Person ( name ) { this . name = name;
}
Person . prototype. sayHello = function ( ) { console. log ( ` Hello, my name is ${ this . name} ` ) ;
} ;
const person1 = new Person ( "Alice" ) ;
person1. sayHello ( ) ;
对象字面量扩展方法(直接在对象上添加方法) 介绍 :对于单个对象,可以直接在对象字面量定义中添加方法。这种方法简单直接,适用于创建一次性的、具有特定行为的对象。示例 :
const myObject = { property : "value" , myMethod : function ( ) { console. log ( "This is my method" ) ; }
} ;
myObject. myMethod ( ) ;
Object.assign()
方法扩展(合并对象并添加方法) 介绍 :Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。可以利用它来扩展一个对象,添加新的方法或覆盖现有属性。示例 :
const baseObject = { existingProperty : "existing value"
} ;
const newMethods = { newMethod : function ( ) { console. log ( "This is a new method" ) ; }
} ;
const extendedObject = Object. assign ( { } , baseObject, newMethods) ;
extendedObject. newMethod ( ) ;
class
语法扩展方法(在类中添加方法) 介绍 :在ES6的class
语法中,可以在类定义内部通过添加方法来扩展类的功能。类的方法会被所有类的实例共享,这种方式更符合面向对象编程的结构。示例 :
class Shape { constructor ( name ) { this . name = name; } area ( ) { console. log ( "The area method needs to be implemented in sub - classes" ) ; }
}
class Circle extends Shape { constructor ( radius ) { super ( "Circle" ) ; this . radius = radius; } area ( ) { return Math. PI * this . radius * this . radius; }
}
const myCircle = new Circle ( 5 ) ;
console. log ( myCircle. area ( ) ) ;
使用Function.prototype.bind()
间接扩展方法(改变函数的this
指向来扩展功能) 介绍 :bind()
方法会创建一个新函数,当这个新函数被调用时,它的this
值会被绑定到指定的对象上。这可以用于在特定的上下文环境中扩展函数的行为。例如,将一个通用的函数绑定到特定的对象上,使其能够访问该对象的属性。示例 :
const obj = { value : 42
} ;
function showValue ( ) { console. log ( this . value) ;
}
const boundShowValue = showValue . bind ( obj) ;
boundShowValue ( ) ;
使用装饰器模式(在函数外层包装新功能)扩展方法 介绍 :装饰器模式是一种结构型设计模式,在JavaScript中可以用来在不修改原函数的基础上,动态地给函数添加新的功能。它通过创建一个包装函数,在包装函数内部调用原函数,并在调用前后添加额外的逻辑。示例 :
function withLogging ( func ) { return function ( ) { console. log ( "Function is about to be called" ) ; const result = func . apply ( this , arguments) ; console. log ( "Function has been called" ) ; return result; } ;
}
function addNumbers ( a, b ) { return a + b;
}
const loggedAddNumbers = withLogging ( addNumbers) ;
console. log ( loggedAddNumbers ( 3 , 5 ) ) ;
使用Proxy
对象扩展方法(拦截操作并添加自定义行为) 介绍 :Proxy
对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。可以利用它来拦截对对象方法的调用,并在调用前后添加扩展的逻辑,例如记录方法调用次数、添加权限验证等。示例 :
const target = { myMethod : function ( ) { console. log ( "Original method called" ) ; }
} ;
const handler = { apply : function ( target, thisArg, argumentsList ) { console. log ( "Before method call" ) ; const result = target. myMethod . apply ( thisArg, argumentsList) ; console. log ( "After method call" ) ; return result; }
} ;
const proxiedObject = new Proxy ( target, handler) ;
proxiedObject. myMethod ( ) ;
扩展方法应用选择
考虑代码的组织和维护性 基于原型(prototype
)扩展 适用场景 :当你在构建面向对象的JavaScript代码,并且希望在多个对象实例之间共享方法时,使用prototype
扩展是很好的选择。例如,在创建自定义对象类型(如Person
、Car
等)时,通过prototype
为所有实例添加通用的方法(如Person.prototype.sayHello
)可以减少内存占用,因为方法在原型链上共享,而不是每个实例都有一份独立的方法副本。示例 :
function Vehicle ( type ) { this . type = type;
}
Vehicle . prototype. start = function ( ) { console. log ( ` The ${ this . type} is starting. ` ) ;
} ;
const car = new Vehicle ( "car" ) ;
const truck = new Vehicle ( "truck" ) ;
car. start ( ) ;
truck. start ( ) ;
对象字面量扩展 适用场景 :对于简单的、一次性的对象,对象字面量扩展很方便。如果只是需要为一个特定的、独立的对象添加一些方法,这种方式可以让代码在局部范围内清晰地定义对象及其行为。示例 :
const userSettings = { fontSize : 14 , color : "black" , updateFontSize : function ( newSize ) { this . fontSize = newSize; console. log ( ` Font size updated to ${ this . fontSize} ` ) ; }
} ;
userSettings. updateFontSize ( 16 ) ;
class
语法扩展 适用场景 :在使用现代JavaScript开发大型应用程序,遵循面向对象编程原则时,class
语法提供了一种更结构化、更易读的方式来定义对象和扩展方法。它使得代码组织类似于传统的面向对象语言,便于团队协作和代码维护。特别是在处理继承关系(如extends
关键字)时,class
语法更加清晰。示例 :
class Animal { constructor ( name ) { this . name = name; } makeSound ( ) { console. log ( "Generic animal sound" ) ; }
}
class Dog extends Animal { constructor ( name ) { super ( name) ; } makeSound ( ) { console. log ( "Woof!" ) ; }
}
const myDog = new Dog ( "Buddy" ) ;
myDog. makeSound ( ) ;
考虑功能需求和灵活性 Object.assign()
扩展 适用场景 :当需要合并多个对象的属性和方法,或者从一个对象中复制属性和方法到另一个对象时,Object.assign()
很有用。这种方式在配置对象、创建具有默认值和自定义值组合的对象等场景下表现出色。示例 :
const defaultSettings = { theme : "light" , fontSize : 16
} ;
const userPreferences = { fontSize : 18
} ;
const finalSettings = Object. assign ( { } , defaultSettings, userPreferences) ;
console. log ( finalSettings) ;
Function.prototype.bind()
间接扩展 适用场景 :如果需要改变函数内部this
的指向,将函数绑定到特定的对象上下文,bind()
方法是合适的选择。在事件处理、回调函数等场景中,经常需要确保函数在正确的this
上下文中执行。示例 :
const button = document. createElement ( "button" ) ;
button. textContent = "Click me" ;
const obj = { message : "Button clicked"
} ;
button. addEventListener ( "click" , function ( ) { console. log ( this . message) ;
} . bind ( obj) ) ;
document. body. appendChild ( button) ;
装饰器模式扩展 适用场景 :当你想要在不修改原有函数代码的基础上,为函数添加额外的功能,如日志记录、性能监控、权限验证等,装饰器模式是很好的选择。它提供了一种灵活的方式来动态地增强函数的功能。示例 :
function logExecutionTime ( func ) { return function ( ) { const startTime = Date. now ( ) ; const result = func . apply ( this , arguments) ; const endTime = Date. now ( ) ; console. log ( ` Function ${ func. name} took ${ endTime - startTime} ms to execute ` ) ; return result; } ;
}
function add ( a, b ) { return a + b;
}
const loggedAdd = logExecutionTime ( add) ;
console. log ( loggedAdd ( 3 , 5 ) ) ;
Proxy
对象扩展 适用场景 :如果需要对对象的操作(如方法调用、属性访问等)进行拦截,并添加自定义的逻辑,如数据验证、访问控制等,Proxy
对象是合适的选择。它提供了一种强大的机制来扩展对象的行为,并且可以在运行时动态地改变对象的行为。示例 :
const user = { name : "John" , age : 30
} ;
const userProxy = new Proxy ( user, { get ( target, property) { console. log ( ` Accessing property ${ property} ` ) ; return target[ property] ; } , set ( target, property, value) { console. log ( ` Setting property ${ property} to ${ value} ` ) ; target[ property] = value; return true ; }
} ) ;
console. log ( userProxy. name) ;
userProxy. age = 31 ;
大型项目 中扩展方法应用选择
遵循面向对象设计原则(使用class
和prototype
) class
语法 适用场景 :在大型项目中,如果要构建复杂的对象层次结构,class
语法是首选。例如,在一个大型的Web应用程序的用户界面组件库中,每个组件可以定义为一个类。像React
组件(虽然它有自己的基于类的旧语法和函数式组件语法)或Vue
组件的类定义,通过class
可以清晰地定义组件的属性、方法和生命周期钩子等。示例 :假设我们在构建一个简单的UI组件库,有一个基础的Button
类。
class Button { constructor ( text ) { this . text = text; this . enabled = true ; } click ( ) { if ( this . enabled) { console. log ( ` Button " ${ this . text} " clicked. ` ) ; } else { console. log ( ` Button " ${ this . text} " is disabled. ` ) ; } } disable ( ) { this . enabled = false ; }
}
const myButton = new Button ( "Submit" ) ;
myButton. click ( ) ;
myButton. disable ( ) ;
myButton. click ( ) ;
prototype
扩展(与class
结合使用) : 适用场景 :对于一些通用的、不依赖于特定类实例状态的方法,可以通过prototype
来扩展。例如,在处理数据模型类时,可能有一些工具方法用于数据验证或格式化,这些方法可以放在原型上。假设我们有一个User
数据模型类,通过prototype
为其添加一个验证电子邮件格式的方法。示例 :
function User ( name, email ) { this . name = name; this . email = email;
}
User . prototype. validateEmail = function ( ) { const emailRegex = / ^ [ a- zA - Z0 - 9_. + - ] + @[ a- zA - Z0 - 9 - ] + \. [ a- zA - Z0 - 9 - . ] + $/ ; return emailRegex. test ( this . email) ;
} ;
const user1 = new User ( "Alice" , "alice@example.com" ) ;
const user2 = new User ( "Bob" , "invalid_email" ) ;
console. log ( user1. validateEmail ( ) ) ;
console. log ( user2. validateEmail ( ) ) ;
配置和对象合并(使用Object.assign()
) 适用场景 :在大型项目中,配置管理是很重要的部分。Object.assign()
可用于合并默认配置和用户自定义配置。例如,在一个项目的主题系统中,有默认的主题配置(颜色、字体等),可以使用Object.assign()
将用户选择的主题配置合并进去。示例 :
const defaultTheme = { primaryColor : "blue" , secondaryColor : "gray" , fontSize : "16px"
} ;
const userTheme = { secondaryColor : "green" , fontWeight : "bold"
} ;
const finalTheme = Object. assign ( { } , defaultTheme, userTheme) ;
console. log ( finalTheme) ;
函数增强和中间件模式(装饰器模式和Function.prototype.bind()
) 装饰器模式 适用场景 :在大型项目的请求处理、日志记录或权限控制等场景中,装饰器模式可以有效地增强函数功能。例如,在一个后端API服务中,对于每个API端点的处理函数,可以使用装饰器来添加日志记录和权限验证功能。示例 :假设我们有一个简单的函数用于获取用户数据,使用装饰器来添加日志记录。
function logFunctionCall ( func ) { return function ( ) { console. log ( ` Calling function ${ func. name} ` ) ; return func . apply ( this , arguments) ; } ;
}
function getUserData ( userId ) { console. log ( ` Fetching data for user ${ userId} ` ) ; return { id : userId, name : "John Doe" } ;
}
const loggedGetUserData = logFunctionCall ( getUserData) ;
loggedGetUserData ( 123 ) ;
Function.prototype.bind()
适用场景 :在处理事件绑定或异步操作的回调函数时,bind()
方法可以确保函数在正确的上下文中执行。例如,在一个大型的JavaScript应用程序中,当处理用户界面交互事件,如按钮点击事件,需要将事件处理函数绑定到正确的组件实例上。示例 :
class Counter { constructor ( ) { this . count = 0 ; this . incrementButton = document. createElement ( "button" ) ; this . incrementButton. textContent = "Increment" ; this . incrementButton. addEventListener ( "click" , this . increment . bind ( this ) ) ; document. body. appendChild ( this . incrementButton) ; } increment ( ) { this . count++ ; console. log ( ` Count: ${ this . count} ` ) ; }
}
new Counter ( ) ;
代理和拦截(使用Proxy
) 适用场景 :在大型项目的数据访问层或对象交互层,如果需要对对象的操作进行细粒度的控制和拦截,Proxy
是很好的选择。例如,在一个数据缓存系统中,可以使用Proxy
来拦截对数据对象的访问,实现自动缓存和读取缓存的功能。示例 :
const cache = new Map ( ) ;
function createCachedObject ( target ) { return new Proxy ( target, { get ( target, property) { if ( ! cache. has ( property) ) { cache. set ( property, target[ property] ) ; } return cache. get ( property) ; } } ) ;
}
const dataObject = { value1 : 10 , value2 : 20
} ;
const cachedDataObject = createCachedObject ( dataObject) ;
console. log ( cachedDataObject. value1) ;
console. log ( cachedDataObject. value1) ;
小型项目中 扩展应用
对象字面量扩展方法 适用场景 当项目规模较小,逻辑相对简单,且主要是处理一些独立的、一次性的对象时,对象字面量扩展方法是很方便的选择。例如,在一个简单的网页表单验证脚本中,你可能只需要为一个包含表单字段信息的对象添加验证方法。 示例
const formData = { username : "" , password : "" , validateUsername : function ( ) { return this . username. length > 0 ; } , validatePassword : function ( ) { return this . password. length >= 6 ; }
} ;
console. log ( formData. validateUsername ( ) ) ;
console. log ( formData. validatePassword ( ) ) ;
Function.prototype.bind()
方法 适用场景 在小型项目的事件处理场景中,尤其是当你需要确保函数内部的this
指向正确的对象时,bind()
方法非常实用。例如,在一个小型的图片轮播插件中,你可能有一个用于切换图片的函数,需要将其绑定到轮播组件对象上,以正确地访问和更新组件的状态。 示例
const carousel = { currentIndex : 0 , images : [ "image1.jpg" , "image2.jpg" , "image3.jpg" ] , nextImage : function ( ) { this . currentIndex++ ; if ( this . currentIndex >= this . images. length) { this . currentIndex = 0 ; } console. log ( ` Showing ${ this . images[ this . currentIndex] } ` ) ; }
} ;
const nextButton = document. createElement ( "button" ) ;
nextButton. textContent = "Next" ;
nextButton. addEventListener ( "click" , carousel. nextImage . bind ( carousel) ) ;
document. body. appendChild ( nextButton) ;
简单的原型(prototype
)扩展(对于基于函数构造器的情况) 适用场景 如果小型项目中使用了基于函数构造器创建的对象,并且有一些通用的方法需要被这些对象共享,原型扩展是一个不错的方式。比如,在一个简单的游戏项目中,有多种游戏角色都需要有一个移动的方法,可以通过原型来添加这个共享的方法。 示例
function Character ( name, x, y ) { this . name = name; this . x = x; this . y = y;
}
Character . prototype. move = function ( dx, dy ) { this . x += dx; this . y += dy; console. log ( ` ${ this . name} moved to ( ${ this . x} , ${ this . y} ) ` ) ;
} ;
const player = new Character ( "Player" , 0 , 0 ) ;
const enemy = new Character ( "Enemy" , 5 , 5 ) ;
player. move ( 1 , 1 ) ;
enemy. move ( - 1 , - 1 ) ;