JavaScript高阶函数
在说javascript高阶函数之前,有这样一个案例,有一个对象数组,我要计算数组中所有对象的总金额问题。
let books = [{
id: 1,
name: "《算法导论》",
date: "2006-9",
price: 85.12,
count: 2
},
{
id: 2,
name: "《UNIX编程艺术》",
date: "2006-2",
price: 59.28,
count: 1
},
{
id: 3,
name: "《编程珠玑》",
date: "2008-10",
price: 39.2,
count: 1
}
]
普通方法: 普通for循环/for in /for of
let total=0
for (let i = 0; i < books.length; i++) {
total += books[i].count*books[i].price;
}
return total;
--------------------------------------------------------------------------
for (let i in books) {
total += books[i].count*books[i].price;
}
return total;
--------------------------------------------------------------------------
for (const book of books) {
total += book.count*book.price;
}
return total;
每次计算都需要循环遍历,代码写起来很麻烦,有没有一种函数,直接给我把值全都计算好呢。当然有,和java stream的reduce规约操作一样,js一样有reduce规约操作。
reduce–高阶函数之规约操作
语法:
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number)
可以看到,这个方法中需要两个参数:
普通数组
先看下普通的数组,求数组中所有数据之和,因为是普通值类型数组,一般情况下只传入一个回调函数就完全够用,previousValue会被当成初始值;如果有初始值要求设置上就行了
普通回调函数
let arrs = [10, 20, 30, 111, 500,34]
function arrsReduce(){
return arrs.reduce(function(preValue, n){
return preValue + n;
})
}
箭头函数回调
let arrs = [10, 20, 30, 111, 500,34]
function arrsReduce(){
return arrs.reduce((preValue, n) => preValue + n);
}
对象数组
还使用上面说到的计算数组中所有对象的总金额问题。
箭头函数回调
function finalTotal(){
return books.reduce((preValue, n) => prevalue + n.count*n.price, 0);
}
会发现,上面用遍历写了一大堆,结果这边用一个规约操作,直接一行代码全搞定。
当对对象数组进行规约操作计算某总值时,一定要注意preValue 和n 分别代表什么,如上preValue是上一次计算出的总额,那么preValue必须是number类型的。而n是一个对象类型,一定要知道。
那么刚开始执行时,preValue不能是第一个元素,只能是初始值0。
filter–高阶函数之过滤操作
filter回调函数中有一个要求:必须要返回一个boolean值
返回true时,函数内部会将这次回调的参数加入到新的数组中,false则被过滤掉
普通回调函数
let arrs = [10, 20, 30, 111, 500,34]
function arrFilter(){
return arrs.filter(function(n){
return n > 100
});
}
箭头函数回调
不明白箭头函数的可以查下资料学习下,这里简单复习下
当函数中参数只有一个时,()可以省略不写;当函数体内语句只有一句时 {} 可以不写,并且return也可以不写。
let arrs = [10, 20, 30, 111, 500,34]
function arrFilter(){
return arrs.filter(x => x > 100);
}
map–高阶函数之映射操作
可单独对数组中每个元素进行操作,比如每个元素都 *2等,最后会生成新的数组
普通函数回调
let arrs = [10, 20, 30, 111, 500,34]
function arrMap(){
return arrs.map(function(n){
return n * 2
});
}
箭头函数回调
let arrs = [10, 20, 30, 111, 500,34]
function arrMap(){
return arrs.map(n => n*2);
}
v-model
text
双向绑定,数据和视图的绑定
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好"
}
})
</script>
radio
使用v-model绑定同一个属性后,name可以省略(分组)
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
{{sex}}
</div>
<script src="./vue/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
sex: '女'
}
})
</script>
checkbox
<div id="app">
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意
</label>
<h2>你选择的是{{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
<input type="checkbox" value="篮球" v-model="hobby">篮球
<input type="checkbox" value="足球" v-model="hobby">足球
<input type="checkbox" value="乒乓球" v-model="hobby">乒乓球
<h2>您的爱好是{{hobby}}</h2>
</div>
<script src="./vue/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
isAgree: false,
hobby: []
}
})
</script>
select
<div id="app">
<select name="abc" id="" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="橘子">橘子</option>
</select>
<h2>{{fruit}}</h2>
<select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="橘子">橘子</option>
</select>
<h2>{{fruits}}</h2>
</div>
<script src="./vue/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
fruit: '香蕉',
fruits: []
}
})
</script>
修饰符
- .lazy : 绑定的属性懒加载,只有失去焦点或者用户输入回车时才去加载
- .number : 表示绑定的值是number类型
- .trim : 自动去除两端空格
组件化
就是将一个大的整体,拆分成一个个的小整体的组件。
基本使用
<div id="app">
<my-cpn></my-cpn> // 调用组件
</div>
<script src="./vue/vue.js"></script>
<script>
Vue.component('my-cpn', {
template: ` // params2: 定义模板
<div>
<h2>标题</h2>
</div>`
})
const app = new Vue({
el:"#app"
})
</script>
全局组件和局部组件
Vue.component('my-cpn', {
template: `
<div>
<h2>标题</h2>
</div>`
})
const app = new Vue({
el:"#app"
})
-------------------------------------------------------
const app = new Vue({
el:"#app",
components:{
cpn:{
template: `
<div>
<h2>标题</h2>
</div>`
}
}
})
组件模板抽离写法
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<h2>标题</h2>
</template>
<script src="./vue/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
components:{
cpn:{
template: "#cpn"
}
}
})
</script>
请注意:template标签中必须有且只有一个root级的标签,否则会报错。
以上 <template></template>中只写了一句<h2></h2>,如果有多个元素,请一定要使用 <div></div>或其他元素将它们包裹起来,如下:
<template id="cpn">
<div>
<h2>标题</h2>
<p>内容</p>
</div>
</template>
组件中的数据存放问题
问题:组件中能否访问vue实例中的数据
组件如果想要访问vue实例中的数据,不能直接去访问,需要通过模板进行参数的传递。同vue实例中的data属性一样,组件也可以有存储数据的地方,但是与vue实例不同的是,组件中的data只能是一个函数,而不能是一个属性 。
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<h2>{{title}}</h2>
</template>
<script src="./vue/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
components:{
cpn:{
template: "#cpn",
// 组件中定义一个 data函数,用于返回数据
data(){
return { // 返回一个数据为对象类型
title: "标题"
}
}
}
}
})
问题:为什么组件中的data必须是函数
可以从vue组件的实例化角度考虑,一个vue组件如果实例化多次,那么我们会想多个实例化对象是相互独立,互不影响的,data设置为函数,然后data函数中每次返回一个新的对象。那么vue组件多个实例使用的都不是同一个data的对象,自然就实现了多实例之间数据的互不影响。
父子组件及通信
每个组件中都有 components 属性,如果要套子组件,那么直接在组件的 components 中继续加组件即可。其实vue实例也可以看成为一个组件。
要注意:父子组件不可以跨级调用,除非在全局组件作用域中。
通信两种方式:
- props:通过props向子组件传送数据
- 通过事件向父组件发送消息 $emit
props : 父向子传递数据
props可以是一个数组,也可以是一个对象(可加数据类型验证等等)推荐使用
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{cmovies}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn ={
template: "#cpn",
props:{
cmovies: Array,
cmessage: {
type: String,
default: 'aaa'
}
}
}
const app = new Vue({
el:"#app",
data:{
movies:["海王", "加勒比海盗", "海尔兄弟"],
message: "hello"
},
components:{
cpn
}
})
</script>
如果是Object或者Array类型,那么很可能高版本的vue不支持此写法
props:{
cmovies:{
type: Array,
default: []
}
}
提示报错,Invalid default value for prop: .. Props with type Object/Array must use a factory function to return the default value
写法改为:
props:{
cmovies: {
type: Array,
default(){
return []
}
}
}
写法总结图:
props中驼峰标识
假如我定义的props中有驼峰式写法
props:{
cInfo:{
type: Object,
default(){
return {}
}
}
}
<cpn :cInfo="info"></cpn>
<cpn :c-info="info"></cpn>
$emit子组件向父组件发消息(自定义事件)
<div id="app">
<cpn @cateclick="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="cate in categories" @click="btnClick(cate)">{{cate.name}}</button>
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data(){
return {
categories: [
{id: 'aaa',name: "热门推荐"},
{id: 'bbb',name: "手机数码"},
{id: 'ccc',name: "家用家电"},
{id: 'ddd',name: "电脑办公"}
]
}
},
methods:{
btnClick(cate){
this.$emit("cateclick", cate)
}
}
}
const app = new Vue({
el:"#app",
components:{
cpn
},
methods:{
cpnClick(cate){
console.log(cate)
}
}
});
</script>
结合v-model实现父子通信
:value + @input实现
首先要知道,v-model的实现其实就是:value 绑定和@input 传入来完成的
实现需求:
子组件中有一个输入框,需要把输入框中输入和数据及时更新到父组件的属性中,由于子组件中如果v-model来绑定props,vue不支持这种写法 ,所以我们要绑定子组件中的 data函数中的参数,并且当输入框输入数据后,通过自定义事件将值传递给父组件。
由于涉及到输入(事件)、展示(属性) ,所以不使用v-model进行绑定,而是使用底层的:value 和@input 来配合完成。一般来讲,这种需求比较难遇到。
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<template id="cpn">
<div>
num1:<input :value="dnumber1" @input="num1change"/> data:{{dnumber1}} props:{{number1}}<br>
num2:<input :value="dnumber2" @input="num2change"/> data:{{dnumber2}} props:{{number2}}
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn = {
template:"#cpn",
props:{
number1: Number,
number2: Number
},
data(){
return{
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods:{
num1change(event){
this.dnumber1 = event.target.value;
this.$emit('num1change', this.dnumber1)
},
num2change(event){
this.dnumber2 = event.target.value;
this.$emit('num2change', this.dnumber2)
}
}
}
const vue = new Vue({
el:"#app",
data:{
num1: 1,
num2: 2
},
components:{
cpn
},
methods:{
num1change(value){
this.num1 = parseInt(value)
},
num2change(value){
this.num2 = parseInt(value)
}
}
})
</script>
watch监听实现
在子组件中定义watch监听
watch:{
dnumber1(newValue){
this.dnumber1 = newValue;
this.$emit('num1change', this.dnumber1)
}
}
父访问子&子访问父
- 父访问子 : $children或者 $refs
- 子访问父 : $parent
父访问子
父访问子:使用 $children(不推荐使用)
$children是获取到子组件的数组。数组中是所有的子组件,形成一个树形结构。
<div id="app">
<cpn></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn = {
template:"#cpn",
methods: {
showMessage(){
console.log("showMessage")
}
}
}
const app = new Vue({
el:"#app",
components:{
cpn
},
methods: {
btnClick(){
console.log(this.$children);
this.$children[0].showMessage();
}
}
})
</script>
父访问子: 使用 $refs (推荐使用)
直接根据ref能拿到子组件对象,注意是对象,不是集合
<div id="app">
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn = {
template:"#cpn",
methods: {
showMessage(){
console.log("showMessage")
}
}
}
const app = new Vue({
el:"#app",
components:{
cpn
},
methods: {
btnClick(){
console.log(this.$refs.aaa);
this.$refs.aaa.showMessage();
}
}
})
</script>
子访问父
要注意层级关系,当父组件是 Vue实例的时候,那么父组件的类型就是一个 Vue类型,如果是父组件还是一个组件类型,那么父组件的类型就是 VueComponet。注意:获取到的是对象,不是数组
$parent,不推荐使用
由于会打乱结构,与组件独立这一观念冲突,一般不会去使用 $parent,更多是使用 $root直接获取vue实例对象。($parent就不再演示)
$root,直接获取vue实例
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="./vue/vue.js"></script>
<script>
const cpn = {
template:"#cpn",
methods: {
btnClick(){
console.log(this.$root);
console.log(this.$root.message)
}
}
}
const app = new Vue({
el:"#app",
data:{
message: "你好"
},
components:{
cpn
}
})
</script>
|