JavaScript 学习记录
Mobx
设计哲学
MobX 的设计哲学概括起来就一句话:Anything that can be derived from the application state, should be derived. Automatically. (任何可以从应用状态中派生的内容,都应当自动地被派生。)
MobX中【派生】的含义比较广泛,包括:用户接口(UI),如组件、页面、图表 - 派生数据(computed data),如从数组中计算b得到的数组长度 - 副作用(side effect),如发送网络请求、设置定时任务、打印日志等。
MobX状态响应模型
状态响应模型概括为三个要素:定义状态、响应状态、修改状态。
MobX
通过observable
来定义可观察状态,它接受任意JS值(Object、Array、Map、Set),返回原始数据的代理对象,代理对象与原始数据具有相同的接口,可以把代理对象当做原始数据使用。
// 定义状态
const store = observable({
count: 0
});
Mbox
中通过autorun
来定义状态变化时要执行的响应操作,它接受一个函数。此后,每当observable
中定义的状态发生变化时,MobX
都会立即执行该函数。
// 响应状态
autorun(() => {
console.log("count:", store.count);
});
MobX状态派生模型
Mobx
的状态操作分为两类:带副作用(如打印日志,渲染UI,请求网络等)和不带副作用(如计算数组的长度)。MobX
将这些操作统称为派生(Dervations),即可从应用状态中派生出来任何内容。
其中不带副作用的操作是一个纯函数,一般是根据当前状态计算返回一个新的值。为了区分带副作用和不带副作用的两类情况,MobX 将 Derivations 概念细分成两个概念 Reactions 和 Computed values 来区分二者。此外,MobX 还提供了一个可选的概念 Action 来表示对 State 的修改操作,用于约束和预测应用状态的修改行为。这些概念汇总在一起如下图:
<html>
<body>
<div id="container"></div>
</body>
</html>
index.js
import { observable, autorun, computed, action } from "mobx"
const container=document.querySelector("#container");
//state
const store=observable({
count:0
});
//Actions
window.increaseCount=action(()=>store.count);
//Reactions
autorun(()=>{
containerEl.innerHTML=`
<span>${store.count} * 2 = ${doubleCount.get()}</span>
<button onclick='window.increaseCount()'>+1</button>
`;
})
// 查看npm全局安装包
npm list g depth 0
Mobx + React 实例
文件结构
├─public
└─src
├─assets
│ └─scss
├─components
│ └─todoListView
├─pages
│ ├─home
│ └─login
└─store
注意要点
mobx V6版本需要在store中使用
makeObservable(this)
函数实现状态变更引起视图变化import {action, observable,makeObservable} from "mobx"; class HomeStore{ constructor(){ makeObservable(this); } @observable homeNum=0; @action addNum(){ this.homeNum+=1; } @action lessNum(){ this.homeNum+=-1; } } export default HomeStore;
我的项目地址
JavaScript装饰器语法
内容学习于JavaScript装饰器(Decorator):它是什么以及如何使用? - 知乎 (zhihu.com)
javaScript
中,装饰器是一种用于修饰类(class)、方法、属性的装饰。
什么是装饰器
装饰器以其最简单的形式只是将一段代码和另一段代码包装在一起的一种方式——实际上就是“修饰”它。
//只需调用一个函数包装另一个函数
function fn(name){
console.log(`Hello,${name}`);
}
function decorator(fn) {
return function() {
console.log('Starting')
const result = fn.apply(this, arguments)
console.log('Finished')
return result
}
}
const wrapped=decorator(fn)
fn("Tom")
/*
output:
Hello,Tom
*/
//通过包装函数进行一些日志记录
wrapped("Tom")
/*
output:
Starting
Hello,Tom
Finished
*/
使用Js装饰器
装饰器在前缀添加@
符号,在类中同时使用多个装饰器,它们将按照声明顺序应用。
@log
@immutable
class Example {
@observable price = 0
@observable amount = 1
constructor(price) {
this.price = price
}
@computed get total() {
return this.price * this.amount;
}
}
@log
记录对类的所有访问日志信息@immutable
可以使类不可变-在新的实例中使用Object.freeze使其不可改变@observable
该属性为观察属性@computed
该属性为计算属性
装饰器的应用范围
当前仅支持类和类的成员上的装饰器类型。属性 方法 getter setter
类装饰器
将类修饰符一次性应用于整个类定义。装饰器函数使用单个参数调用,该参数是要装饰的构造函数。
日志记录构造函数参数实例
function log(Class) {
return (...args) => {
console.log(args)
return new Class(...args)
}
}
在这里,我们接受一个类作为参数,并返回一个将用作构造函数的新函数。这里的装饰器只打印了构造函数中的参数,并返回使用这些参数构造的类的新实例。 然后,我们在类中使用这个log函数
@log
class Animal {
constructor(name, age) {
this.name = name
this.age = age
}
}
const cat = new Animal('Hello kitty', 2)
// ["Hello kitty", 2]
console.log(cat.name)
// Hello kitty
如果想为装饰器@log
传入自定义参数,则需要在log
中返回一个本身就是装饰器的函数:
function log(name) {
return function decorator(Class) {
return (...args) => {
console.log(`log for ${name}: `, args)
return new Class(...args)
}
}
}
@log('cat')
class Animal {
constructor(name, age) {
this.name = name
this.age = age
}
}
const cat = new Animal('Hello kitty', 2)
// log for cat: ["Hello kitty", 2]
console.log(cat.name)
// Hello kitty
类成员装饰器
类成员装饰器应用于类中的单个成员-无论它们是属性,方法,getter或setter。该装饰器被调用时需要有以下三个参数:
-
target:要修改属性的对象。
-
name:要修改的属性的名称。
-
descriptor:要修改的属性描述符。从本质上讲,这是应该传递给
Object.defineProperty
的对象。
类属性装饰器
@readonly
function readonly(target,name,descriptor){
descriptor.writable=false;
return descriptor;
}
这里将对象属性描述符writable
标志更改为false。然后将其用于类属性,如下所示:
class Animal {
@readonly age = 2
}
const cat = new Animal()
// {age: 2}
类方法装饰器
function log(target, name, descriptor) {
const original = descriptor.value
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`log for args: ${args}`)
try {
return original.apply(this, args)
} catch (e) {
console.log(`Error: ${e}`)
throw e
}
}
}
return descriptor
}
类方法中使用
class Animal {
constructor(name) {
this.name = name
}
@log
sayHello(name) {
console.log(`Hello ${name}, I'm ${this.name}`)
}
}
const cat = new Animal('Hello kitty')
cat.sayHello('Jack')
// log for args: Jack
// Hello Jack, I'm Hello kitty
同样,我们可以为装饰器@log
传入自定义参数,则需要在log中返回一个本身就是装饰器的函数:
function log(name) {
return function decorator(target, name, descriptor) {
const original = descriptor.value
if (typeof original == 'function') {
descriptor.value = function(...args) {
console.log(`log for ${name}: `, args)
// ...
}
}
return descriptor
}
}
但是当我们将装饰器log进行分解时,我们会得到: 一个具有单个参数名为name的函数 该函数返回了一个装饰器函数。
JavaScript 中 call()、apply()、bind() 的用法 | 菜鸟教程 (runoob.com)
Call函数
可以用来调用所有者对象作为参数的方法。通过call()
,可以使用属于另一个对象的方法。
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName:"John",
lastName: "Doe",
}
var person2 = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(person2); // 将返回 "Steve Jobs"
Apply函数
通过apply()
方法,可以编写用于不同对象的方法。
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName: "Bill",
lastName: "Gates",
}
person.fullName.apply(person1);
// 将返回 "Bill Gates"
call()和apply()之间的区别
call() 方法分别接受参数。
apply() 方法接受数组形式的参数。
如果要使用数组而不是参数列表,则 apply() 方法非常方便。
var person = { fullName: function(city, country) { return this.firstName + " " + this.lastName + "," + city + "," + country; } } var person1 = { firstName:"John", lastName: "Doe" } person.fullName.apply(person1, ["Oslo", "Norway"]);
JavaScript 的this指向
1. 全局环境中的this
非严格模式下,全局中的this是指向window的
console.log(this);
2. 在事件绑定中的this
js中不可避免的就是需要对dom元素进行事件的绑定,那么事件中的this就是指向被绑定对象本身。
3. 作为构造函数的调用
一般在对象定义中的this是指向对象本身
4. 如何灵活修改this指向
当一个函数被用来new一个对象的时候,这个函数就被称为构造函数,那么此时构造函数内的this就会指向该实例化的对象本身。
5. 箭头函数和普通函数的this指向
Object
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
参数:target
目标对象
sources
源对象
返回值 目标对象
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
//output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
//output: Object { a: 1, b: 4, c: 5 }
Desc
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象属性。