MVVM架构模式和函数柯里化
1.MVVM架构模式
2.函数柯里化
01-MVVM架构模式【重点】
`概念`: MVVM是一种架构/设计模式。 MVVM是Model,View和ViewModel的简称
Model: 模型层。 提供数据
View: 视图层。 渲染页面
ViewModel: 视图模型层 连接模型层和视图层的桥梁
`核心`:实现了数据的双向绑定。数据驱动视图:当数据发生变化,视图会自动更新。
`Object.defineProperty()实现MVVM架构`:
<div id="app"></div>
<script>
// MVVM核心:数据的双向绑定,数据驱动视图
// Model: 模型层 提供数据
var model = {
uname: '小妲己'
}
// ViewModel: 视图模型层 连接模型层和视图层
/*
考虑:1. 什么时候访问了数据;2. 什么时候更新了数据
*/
// 数据劫持
var _uname = model.uname
Object.defineProperty(model, 'uname', {
get: function () {
// 当访问了被监听的属性时触发
console.log(new Date(), 'uname属性被访问了');
// 注意:这里不能返回被监听的属性,否则会一直触发get方法,变成死循环
// 解决:可以在劫持之前,将被监听的属性赋值给另一个临时变量
return _uname
},
set: function (newVal) {
// 当修改了被监听的属性时触发
console.log(new Date(), 'uname属性被修改了', newVal);
// 修改模型层的数据
_uname = newVal
// 通知视图层更新
watch()
}
})
// View: 视图层 渲染页面
function render() {
app.innerHTML = `<span>${model.uname}</span><button οnclick="changeUname()">戳我呀~</button>`
}
render()
function changeUname() {
// 修改了被监听的属性,会触发set方法
model.uname = '小貂蝉'
}
function watch() {
render()
}
// Object.defineProperty是一个es5方法
// Object.defineProperty(劫持的对象, '监听的属性', {
// get: function () {
// // 当访问了被监听的属性时触发
// },
// set: function () {
// // 当修改了被监听的属性时触发
// }
// })
</script>
`Proxy实现MVVM架构`:
<div id="app"></div>
<script>
// Model: 数据层。 提供数据
var model = {
uname: '小妲己',
age: 18
}
// ViewModel: 数据模型层, 连接数据层和视图层的桥梁
var newModel = new Proxy(model, {
get: function (target, key) {
console.log(target, key);
// 访问了被监听的对象属性时触发
console.log(new Date(), '访问了被监听的对象属性');
return target[key]
},
set: function (target, key, newVal) {
console.log(target, key, newVal);
// 修改了被监听的对象属性时触发
console.log(new Date(), '修改了被监听的对象属性');
// 修改了视图模型层的数据
target[key] = newVal
// 通知视图更新
watch()
}
})
// View: 视图层. 渲染页面
function render() {
app.innerHTML = `<span>${newModel.uname}</span><span>${newModel.age}</span><button οnclick="changeUname()">戳我呀~</button>`
}
render()
// 用户的交互行为
function changeUname() {
newModel.uname = '小貂蝉'
newModel.age = 19
}
function watch() {
render()
}
// Proxy: es6的数据代理, 是一个构造函数。可以监听整个对象的任意属性的访问或者修改
// new Proxy(代理的对象, {
// get: function (target, key) {
// target: 被监听的对象;
// key: 被监听的对象的属性
// // 访问被监听的对象属性时触发
// },
// set: function (target, key, newVal) {
// target: 被监听的对象;
// key: 被监听的对象的属性
// newVal: 修改的属性值
// // 修改被监听的对象属性时触发
// }
// })
</script>
02-Object.defineProperty()
`概念`:es5中一个用来实现数据劫持的方法,具有唯一性:es5没有其他方法可以替代
`问题`: Object.defineProperty这个方法自身具有一些缺陷
`解决`: es6 Proxy 数据代理
`语法`: Object.defineProperty(劫持的对象, '监听的属性', {
get: function() {
// 访问了被监听的对象的属性时触发
},
set: function(newVal) {
// 修改了被监听的对象的属性时触发
}
})
03-Proxy
`概念`: es6中一个用来实现数据代理的方法。
`特点`: 可以监听整个对象任意属性的访问和修改
`语法`: let newModel = new Proxy(代理的对象, {
get: function(target, key) {
// 访问了对象的属性时触发
},
set: function(target, key, newVal) {
// 修改了对象的任意属性时触发
}
})
04-Object相关的静态方法
# Object.create(): 创建一个新的对象,新对象的隐式原型会指向源对象
# Object.assign(): 合并对象|浅拷贝
# Object.defineProperty(): 数据劫持
# Object.keys(): 遍历对象,并返回对象的属性名组成的数组
# Object.values(): 遍历对象,并返回对象的属性值组成的数组
05-hasOwnProperty
`作用`: 判断当前对象中是否有指定的属性,不包含原型上的属性。返回布尔值
let obj = {
uname: '小妲己',
age: 18,
sex: '女'
}
Object.prototype.add1 = '立人科技'
// obj就是Object构造函数的实例对象,自然可以访问Object构造函数原型对象上的属性和方法
console.log(obj.add1); // 立人科技
// 注意:hasOwnProperty这个方法用来判断某个值是否是对象自身的属性,不包含原型对象上的属性
console.log(obj.hasOwnProperty('age')); // true
console.log(obj.hasOwnProperty('hobby')); // false
console.log(obj.hasOwnProperty('add1')); // false
06-函数柯里化【面试题】
`概念`:
'官方解释': 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术
'理解': 把原本接收所有参数的函数转换成只接收部分参数的函数,并且返回一个新函数,这个新函数可以接收剩余参数,并返回最终的结果
`特性`:1. 参数的延迟调用; 2. 参数复用
`示例代码`:
// 注意:递归的使用场景:需要使用重复的代码的时候,但是并不确定要使用多少次的时候
// 功能:计算任意个数的数字的累加和
function currySum() {
// console.log(arguments);
let args = [...arguments] // 将伪数组转换为真数组
let inner = function () {
if (arguments.length === 0) { // 执行到了末尾,就可以计算累加和
return args.reduce((pre, cur) => pre + cur)
} else {
// 没有执行到末尾,将新的一个小括号的数字添加到数组中
args.push(...arguments)
// 并且要返回inner函数
return inner
}
// args.push(...arguments)
// return args.reduce((pre, cur) => pre + cur)
}
return inner
}
// console.log(currySum(2)(3));
console.log(currySum(2)(3)(4)());
console.log(currySum(2)(3)(4)(5)());
console.log(currySum(2)(3)(4)(5)());
console.log(currySum(2)(3)(4)(5)(6)());
console.log(currySum(2)(3)(4, 3, 1)(5)(6, 10, 2)(7)());
// 利用函数柯里化的特性:参数复用。实现了url地址拼接的功能
function uri(protocol) {
return function (hostname, path) {
return `${protocol}${hostname}${path}`
}
}
let curryUri = uri('https://') // 每一次拼接的时候,就只需要调用一次'protocol'这个属性
console.log(curryUri('www.baidu.com/', 'index.html'));
console.log(curryUri('www.sina.com/', 'about.html'));
扩展
闭包:跨作用域调用函数
递归使用场景:需要使用重复的代码的时候,但是不知道使用多少次。函数直接或者间接调用自己,自己调用自己就是递归函数
回调函数:函数内部(自己)调用(自己的)参数传过来的函数
函数调用:函数内部调用其他函数