经过三个月左右的时间,今天项目终于可以进行提测,虽然还有一些问题,但是至少算有一个节点,在此将近期的工作做一次总结,将项目当中存在的问题进行一次汇总,便于后期的查看。
由于涉及到的点很多,目前先在此做统一汇总,后期会再进行相关的分类整理。
项目基础
- 以 Vue 为主要架构,在 PC 端上,通过引入 Bootstrap 进行界面的优化编辑,在数据处理中依然以 Vue 的
双向数据绑
定为主,在样式处理上使用的为scss
(额~不知道为什么网上给出的都是 Sass 的官网,所以就拿简书上的一个教程来参考啦~perishing); - 在 UI 设计上,通过运用
蓝湖
进行UI图的设计,在处理图像以及 ICON 上基于阿里的icon 图标库
来实现图标的选择的; - 后台为 Java 后台,并通过
Node.js
进行页面的显示(说实话,虽然 Node.js 属于 JS 领域,目前我对此也只是做一些简单的服务请求搭建,后期有时间再去学习吧,具体 demo 可在我的 GitHub 查看); - 第三方引入的为
腾讯 IM
接口,来实现相关的数据传递; - 后期将框架转为
ElementUI
(之前是用 bootstrap,后来为了配合 Vue 的开发转为了ElementUI)
编程总结
通过本次的项目编程,通过使用 ESLint
来实现代码的完整性以及界面的统一,使文本编辑更为严谨。本次项目也运用了大量的 ES6
语法,使得页面更为简洁,提高了代码可读性。
在此次项目中,也暴露了自身存在的问题,对于业务逻辑的思考不是很熟练,同时对功能的递进有偏差,从而导致了很多地方存在着业务处理不合理性。业务逻辑问题是一个痛点,在后期的工作学习中应该着重去接触和训练业务逻辑思维。对于 JavaScript 基础在今后的工作中应该多加学习和训练,并做好相关的学习笔记,以便在今后的工作中能够少走弯路。
业务逻辑
通过此次项目,更好的理解了业务逻辑关系以及业务调用。在业务功能中将各个函数单独处理,然后在指定位置进行函数调用,当对函数功能进行处理时只需对对应函数做相关处理即可,然后在需要调用的时候进行函数调用,从而更好的去控制函数的业务逻辑关系以及功能性的修改,彼此函数之间不会产生影响。
在指定调用的时候,尽量使用 debugger
进行代码问题的查找,从而查看数据的传递情况,方便查找问题点以及数据传递的变化,近一步去完善数据传递逻辑。
Vue 总结
Vuex
用于数据双向绑定,便于数据存储和调取,主要依赖于 store
文件夹的数据处理。
// store 文件夹结构
|- store
| |- actions.js
| |- getter.js
| |- index.js
| |- mutation-types.js
| |- mutations.js
| |- state.js
getter.js
export const vueData = state => state.vueData
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
actions,
getters,
state,
mutations,
strict: debug,
plugins: debug ? [createLogger()] : []
})
mutation-types.js
export const SET_VUE_DATA = 'SET_VUE_DATA'
mutations.js
import * as types from './mutation-types'
const mutations = {
[types.SET_VUE_DATA] (state, data) {
state.vueData = data
}
}
export default mutations
state.js
const state = {
vueData: {}
}
export default state
mapGetters
获取 Vuex 中的存储数据,用于调用,可以使用 computed
的模式进行调取和页面渲染。(取)
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'VueData'
])
}
}
mapMutations
存储数据到 Vuex 中,通过对数据进行处理,实现数据的绑定工作。(存)
import { mapMutations } from 'vuex'
export default {
methods:{
/**
* 在此进行函数调用
* 或重新渲染对象可 this.setVueData([])
*/
getData () {
this.setVueData(data)
}
...mapMutations({
setVueData: 'SET_VUE_DATA'
})
}
}
mapAction
import { mapAction } from 'vuex'
export default {
methods: {
...mapAction([
])
}
}
computed
Vue 计算属性:
<template>
<div>{{Num}}</div>
</template>
<script>
export default {
computed:{
Num () {
return
}
}
}
</script>
watch
Vue 监听属性,需要自己手动去写监听的值,会大大的浪费一定性能去做监听这种事情
export default {
computed: {
listenVue () {
return this.$store.state.vuex
}
},
methods: {
Fun () {
console.log()
}
},
watch: {
// 监听计算属性返回值,此处为 Vuex 里的变量值,通过监听到的数据发生变化后,执行对应函数
listenVue () {
console.log()
this.Fun()
}
}
}
父子级传参
父级向子级传参
vue 父组件通过 v-bind:num 来传值给子组件
# 父组件
<template>
<div>
<hello v-bind:num="num"></hello>
<p>{{num}}</p>
</div>
</template>
<script>
export default {
components: {
'Hello': () => import('/hello.vue')
},
data () {
return {
num: 10
}
}
}
</script>
# 子组件
<template>
<div>
<p>{{num}}</p>
</div>
</template>
<script>
export default {
props: ['num'], # 通过props接收父级传递过来的参数
data () {
return {
num: 0
}
}
}
</script>
父组件可以修改内容传递给子组件,子组件不可修改父组件参数。
子级向父级传参
- $emit: 子组件传递参数
- $on: 父组件接收参数
$emit
和 $on
可以传递多个参数,父子级传递参数的位置需要移植才可以获取到相对应的值.
# 子组件
<template>
<div>
<span>{{childValue}}</span>
<button @click="sendChild"></button>
</div>
</template>
<script>
export default {
data () {
return {
childValue: 'data'
}
},
methods: {
sendChild () {
this.$emit('childValue', this.childValue)
# this.$emit('childValue', this.childValue, true)
}
}
}
</script>
# 父组件
<template>
<div>
<child v-on:childValue="childValue"></child>
<p>{{name}}</p>
</div>
</template>
<script>
export default {
components: {
'child': () => import('/child.vue')
},
data () {
return {
name: ''
}
},
methods: {
# childValue (childValue, bool) {
childValue (childValue) {
this.name = childValue
}
}
}
</script>
生命周期
- beforeCreate (新对象诞生)
- created (初始化事件,创建)
- beforeMounted (在 DOM 中呈现对象)
- mounted (DOM准备就绪并放置到页面中)
- beforeUpdate (更新已完成)
- updated (在 DOM 中呈现的更新)
- beforeDestroy (对象准备销毁)
- destroyed (销毁对象,从内存中删除)
关于数据绑定
v-bind
<div style=""></div>
<div v-bind:style="[sty1, sty2]"></div>
<div v-bind:style="styleMap"></div>
data () {
return {
sty1: {
display: 'inline-block'
},
sty2: {
color: 'pink'
},
styleMap: {
display: 'inline-block',
color: 'pink'
}
}
}
v-if && v-show
v-if: 将 dom 元素整个添加或删除
v-show: 为该元素添加 css display:none, dom 元素还在
聊一聊路由
脑子发热看了看路由,盲点比较多…
Link: Vue Router 的params和query传参的使用和区别(详尽)
- $router 路由操作对象,只写对象
- $route 路由操作对象,只读对象
路由数据传参
this.$router.push({
path: 'newApply',
query: {
type: item.key,
typeDesc: item.value
}
})
this.$router.push({
name: 'newApply',
params: {
type: item.key,
typeDesc: item.value
}
})
// 接收参数
this.type = this.$route.query.type // get 请求参数地址栏可见
this.type = this.$route.params.type // post 请求参数地址栏不可见
全局混入
… …
JavaScript总结
原型与原型链
prototype
每个函数对象都有一个 prototype
属性,这个属性是一个指针,指向函数的原型对象
;使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法
原型对象: Object.prototype
原型对象是构造函数的一个实例
proto
每个对象都有的属性,构造器的原型
__proto__ === constructor.prototype
注: constructor (构造函数)函数返回对创建此对象的数组函数的引用,包含一个指向 prototype
属性所在函数的指针
实例的构造函数属性( constructor )指向构造函数
For example:
var a = {}
console.log(a.prototype) // undefined
console.log(a.__proto__) // Object()
var b = function () {}
console.log(b.prototype) // b {}
console.log(b.__proto__) // function () {}
原型链
一条由 __proto__
连起来的链条,递归访问 __proto__
必须最终到头,并且值为 null
对象等级划分
Object 是顶级公民,Function 是一等公民。
原型 继承
原型链的基本实现就是将一个构造函数的实例赋值给另一个构造函数的原型。
通过 Iframe 链接引入方式进行页面引用
# 父层引入iframe页面
<template>
# 设置跳转地址:localhost:8080
<iframe frameborder=0 id="showindex" name="showHere" src="http://localhost:8080" scrolling="auto" style="min-width: calc(100vw - 90px);min-height: calc(100vh - 60px)">
</template>
<script type="javascript">
export default {
data () {
return {
csType: null
}
},
mounted () {
this.$nextTick(() => {
const indexShow = document.getElementById('showindex')
indexShow.onload = function () {
document.getElementById('showindex').contentWindow.postMessage(sessionStorage.getItem('loginInfo'), '*')
}
this.csType = this.$route.query.csType ? this.$route.query.csType : 1
})
}
}
</script>
// 子层iframe
// 需要做对应的事件监听,用于将父层传递过来的数据进行还原,目前是存放在 sessionStorage 中,所以只需要在主界面中将父层传递的数据进行对应解析即可
export default {
mounted () {
window.addEventListener('message', function (MessageEvent) {
// 在此进行对于数据解析
})
}
}
键盘事件(keydown event)
- 监听全局键盘事件
$(document).keydown(function(event){ if (event.keyCode === 13) { alert('it is Enter') } })
- 监听某个组件键盘事件
$('#btn').keydown(function(event){ if (event.keyCode === 13) { alert('it is Enter') } })
- 监听组合键,如 Ctrl + c
$(document).keyup(function(event){ if (event.ctrlKey && event.keyCode === 67) { alert('it is Ctrl + C') } })
addEventListener() 方法
DOM2 级事件:
- 事件捕捉事件
- 处于目标阶段
- 事件冒泡阶段
element.addEventListener(event, function, useCapture)
- event: 事件类型
- function: 事件触发后调用的函数
- useCapture: 布尔值,用于描述事件是冒泡还是捕获,默认为冒泡(true or false)
事件传递的两种方式:冒泡、捕获
- 冒泡: 内部元素事件会被先触发,后触发外部事件(false)
- 捕获: 外部元素事件会被先触发,后触发内部事件(true)
定时器
setTimeout() & setInterval()
都属于异步执行方法。
// 只执行一次
setTimeout(function(){},time)
// 可无限循环执行
setInterval(function(){},time)
// clearTimeout():用于停止循环
const timer = setInterval(() =>{
console.log('hello world')
}, 1000)
// 停止此次循环
clearTimeout(timer)
JSON
JSON: JavaScript 对象表示法(JavaScript Object Notation).
JSON 是存储和交换文本信息的语法,是轻量级的文本数据交换格式,JSON 具有自我描述性,更易于理解.
JSON.parse()
JSON.parse()
方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象.
JSON.parse(text[, reviver])
JSON.parse('{}') // {}
JSON.parse('true') // true
JSON.parse('"foo"') // "foo"
JSON.parse('null') // null
JSON.stringify()
将一个 JavaScript 值(对象或数组)转换为一个 JSON 字符串.
JSON.stringify(value[, replacer [, space]])
JSON.parse() 和 JSON.stringify()
- parse 用于从一个字符串中解析出json对象:
- stringify 用于从一个对象解析出字符串.
var a = {a:1.b:2};
JSON.stringify(a) = {"a":1,"b":2};
拷贝
浅拷贝
拷贝父对象,不会拷贝对象的内部的子对象
深拷贝
完全拷贝了父对象及其子对象
obj1={
p:1,
objSun:{
p:1
}
}
// 最简单的深拷贝方法
obj2 = JSON.parse(JSON.stringify(obj1)) // 深拷贝
splice()
向/从数组中添加/删除项目,然后返回被删除的项目
arrayObject.splice(index,howmany,item1,.....,itemX)
For example:裁剪字符串
获取一个 base64 编码的字符串,将头部的 data:image/jpeg;base64,
裁剪掉
var src = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL'
console.log(src.replace("data:image/jpeg;base64,",""))
//'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL'
Array
filter()
用于把 Array 的某种元素过滤掉,返回剩下的元素
var newArray = arr.filter(callback(element[index[array]])[thisArr])
For example1: 删除偶数,只保留奇数
var arr = [1, 2, 3, 5, 6, 8, 10, 15]
var r = arr.filter(function(x){
return x % 2 !== 0
})
console.log(r) // [1, 3, 5, 15]
find()
返回通过测试的数组的第一个元素的值
array.find(function(value, index, arr),thisValue)
// M O O
for…of
创建一个循环来迭代可迭代的对象, ES6
中引入用于替换 for…in 和 forEach(),可以正确响应 break、continue 和 return 语句
for (variable of iterable) {
statement
}
- variable 每个迭代的属性值被分配给该变量
- iterable 一个具有可枚举属性并可以迭代的对象
forEach()
用于调用数组的每个元素,并将元素传递给回调函数,对于空数组是不会执行回调函数的
const arr = [10, 15, 20, 5]
arr.forEach(obj => {
console.log(obj)
})
// Pattern
[].forEach((value, index, array) => {
// operation
})
reduce()
Link: JS数组reduce()方法详解及高级技巧
arr.reduce(callback,[initialValue])
- callback 执行函数中每个值的函数,包含四个参数
- previousValue 上次调用回调返回值,或提供的初始值
- currentValue 当前被处理元素
- index 当前元素在数组中的索引
- array 调用数组
- initialValue 作为第一次调用callback的第一个参数
简单用法
var arr = [1, 2, 3, 4]
var sum = arr.reduce((x,y) => x+y)
var mul = arr.reduce((x,y) => x*y)
console.log( sum ) // 求和,10
console.log( mul ) // 求乘积,24
高级用法
对象属性求和
var result = [ { subject: 'Math', score: 10 }, { subject: 'Chinese', score: 20 }, { subject: 'English', score: 30 } ] var sum = result.reduce(function(prev, cur) { return cur.score + prev; }, 0) console.log(sum) //60
redeuceRight()
从数组的末尾向前将数组中的数组项做累加
concat()
连接连个数组并返回一个新的数组.
var text = new text("1", "2", "3");
text = text.concat("a", "b", "c");
// text is now ["1", "2", "3", "a", "b", "c"]
join(deliminator = ‘,’)
将数组的所有元素连接成一个字符串.
var text = new text("Wind", "Rain", "Fire");
var list = text.join(" - "); // list is "Wind - Rain - Fire"
push()
在数组末尾添加一个或多个元素,并返回数组操作后的长度.
var text = new text("1", "2");
text.push("3"); // text is now ["1", "2", "3"]
pop()
从数组移出最后一个元素,并返回该元素.
var text = new Array("1", "2", "3");
var last = text.pop();
// text is now ["1", "2"], last = "3"
shift()
从数组移出第一个元素,并返回该元素.
var text = new text ("1", "2", "3");
var first = text.shift();
// text is now ["2", "3"], first is "1"
Promise
目的: 为了我们的代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来
// Pattern
new Promise((resolve, reject) => {
...
} /* executor */ )
// Format
const promise = new Promise((resolve, reject) => {
if (/*异步操作*/) {
// allow
resolve(value)
} else {
// refuse
reject(error)
}
}).then(() => {
// return resolve
},() => {
// return reject
}).catch(() => {
// return error
})
For example 1
function runPromise () {
var p = new Promise(function (resolve,reject) {
setTimeout(function () {
// resolve('success')
const num = Math.ceil(Math.random()*10)
num <= 5 ? resolve('success') : reject('fail')
}, 2000)
})
return p
}
runPromise()
//
runPromise()
.then(
function(data) {
// 此时data为resolve传递过来的值
console.log(data)
},
function(reason, data) {
console.log('reject')
console.log(reason)
}
)
For example 2
function newFun () {
return new Promise((resolve, reject) => {
typeof num === 'number' ? resolve() : reject(new Error())
}).then(() => {
console.log('参数是一个number值')
}, () => {
console.log('参数不是一个number值')
})
}
newFun('hhh').then(newFun(1234))
正则表达式总结
正整数: (/^[1-9]\d*$/),
email: (/^[0-9a-z][0-9a-z-_.]+@([0-9a-z][0-9a-z-]*.)+[a-z]{2,}$/i), // 邮箱
phone: (/^0[0-9]{2,3}[2-9][0-9]{6,7}$/), // 座机手机号码
ydphpne: (/^((13[4-9])|(15[012789])|147|182|187|188)[0-9]{8}$/), // 移动手机号码
allphpne: (/^((13[0-9])|(15[0-9])|(18[0-9]))[0-9]{8}$/), // 所有手机号码
ltphpne: (/^((13[0-2])|(15[56])|(186)|(145))[0-9]{8}$/), // 联通手机号码
dxphpne: (/^((133)|(153)|(180)|(189))[0-9]{8}$/), // 电信手机号码
url: (/^(https?:\/\/)([0-9a-z.]+)(:[0-9]+)?([/0-9a-z.]+)?(\?[0-9a-z&=]+)?(#[0-9-a-z]+)?/i), // 网址
num: (/[^0-9]/), // 数字
cnum: (/[^0-9a-zA-Z_.-]/),
photo: (/.jpg$|.jpeg$|.gif$/i), // 图片格式
row: (/n/ig)
数字和字母: (/[0-9a-zA-Z]/)
Warn 问题
遇到的 Warn 问题汇总,包含样式问题和代码优化问题。
组件问题&&工作中曾遇到的问题(foolish~)
关于 components 引入问题
一个组件中只可有一个 components,不可多次使用
返回上一层
@click="$router.back(-1)"
清除网络缓存
在 console.log 中输入以下内容,用于清除本地数据缓存:
localStorage.clear();
界面热插拔
当跳转页面不在主路径下(或在主路径的子路径)将不实现热插拔效果
清空表单(ElementUI)
name
为我们设置的表单规范,为 Stirng 类型,通过上述代码达到表单数据清空的目的
this.$refs[name].resetFields()
样式问题及细节点
当鼠标浮动到指定位置显示内容
<template>
<p :title="returnName"></p>
</template>
<script>
export default {
data () {
return {
return: 'hello world'
}
}
}
</script>
vertical-align
设置元素的垂直对齐方式。该属性定义行内元素的基线相对于该元素所在行的基线的垂直对齐。允许指定负长度值和百分比值。这会使元素降低而不是升高。在表单元格中,这个属性会设置单元格框中的单元格内容的对齐方式。
vue 绑定值与文本信息拼接的两种写法
<p :title="`文本信息${定义参数}`"></p>
<p :title="'文本信息' + 定义参数"></p>
请求问题
- 请求头必须是大写
meta 标签
- 添加标签页 icon:
<meta property="og:image" content="images/img.png">
- 添加页面描述:
<meta name="description" content="TEXT">
页面自适应
@media screen and (max-width: XXXpx){ … }样式要放在 css 样式表的最下面,不然会被相同样式覆盖.
console
其实这个应该算是一个扩展内容,因为之前没有好好去看关于 console 的具体内容,但是后来发现还是有很多值得学习的地方.
Link:你可能不知道console强大
console输入
console.log() // 输出普通消息
console.info() // 输出提示性消息
console.error() // 输出错误信息
console.warn() // 输出警告信息
console.debug() // 输出调试信息
console.dirxml()
用来显示网页的某个节点(node)所包含的 html/xml 代码
console.count()
统计代码被执行的次数
字符串转换问题
// 默认传递给前端的字符串在需要换行的位置都带有$
const newrow = row.replace(/\$/g, ' \n ')
// 在页面渲染时需要给父层添加css3样式:white-space: pre-wrap;
<!doctype html>
是html5标准网页声明,支持html5标准的主流浏览器都认识这个声明,表示网页采用html5。
代码优化
- 如果有
boolean
问题,可以只用一个命名变量,通过非判断
(!)来进行布尔值的判定 - 如果需要做统一处理,应在
style
中做统一操作,而不应该在界面做数据处理 - 单个函数需要单独编写业务逻辑问题,在需要此函数时,只需要调用其函数名即可,不在一个函数中做两个及其以上的函数处理,以免造成代码污染。另外在单个函数的调用中尽可能使用
状态名(status)
,便于后期统一 - 获取全局数据应在最外层做处理,便于数据的获取,只需要从子级给父级传递触发参数
- 代码数据加载时按照单线程加载顺序进行,对于外链地址需考虑网络问题,若是项目问题需考虑代码的优化问题。在运行代码时,若为全局调取函数时,需在最外层做业务逻辑处理,方便代码引入和后期处理;若是单项目函数调用,只需要在对应位置进行业务逻辑的处理,只是对单问题进行业务分析即可
- 把需要多处做统一处理的函数单独写成一个 JS 文件,在需要的时候进行函数的调用,或将多个公用函数封装到一个 JS 文件里,再在引用的时候单独调用里面对应的函数
- 共用 css/JS 可单独写一个 css/JS 样式/文件,在需要引用的地方引用即可
- 动态获取数据时不能使用点操作符,需要使用
[]
来获取对应数据var eg = { 'abc': 'abc' } var a = 'abc' console.log(eg[a]) // abc console.log(eg.a) // undefined
- 不要将大括号写在行首,避免 JavaScript 将其解释为代码块
扩展
关于算法
之前偶然在网上看到关于算法的相关问题,所以想拿出一个模块来进行算法的整理,也算是对自己的一个扩展了吧~
reverse()
用于颠倒数组中元素的顺序
test1
var arr = new Array(3)
arr[0] = 'Jack'
arr[1] = 'Tom'
arr[2] = 'John'
console.log(arr) // ['Jack', 'Tom', 'John']
console.log(arr.reverse()) // ['John', 'Tom', 'Jack']
test2
var arr = [1, 2, 3, 4, 5, null]
console.log(arr.reverse()) // [null, 5, 4, 3, 2, 1]
trim()
用于去除字符串两侧空白
去除字符串两侧空白
String.prototype.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "")
}
去除字符串左侧空白
String.prototype.ltrim=function(){
return this.replace(/(^\s*)/g,"");
}
去除字符串右侧空白
String.prototype.rtrim=function(){
return this.replace(/(\s*$)/g,"");
}
状态码
| 状态码 | 提示信息 | 含义
| :—- | —— | —
| 200 | OK | 请求成功
| 204 | No Content | 请求成功,但没有任何资源可以返回给客户端(请求无返回数据)
| 206 | Partial Content | 对资源某一部分的请求
| 301 | Moved Permanently | 资源的uri已更新/永久性重定向(请求资源分配到新的 URL,即以后资源所指向的 URL )
| 302 | Found | 资源的URI已临时定位到其他位置了/临时性重定向(更改的 URL 后期可能会改变)
| 303 | See Other | 资源的URI已更新,你是否能临时按新的URI访问( GET )
| 304 | Not Modified | 资源已找到,但未符合条件请求
| 307 | Temporary Redirect | 临时重定向(同 302)
| 400 | Bad Request | 服务器端无法理解客户端发送的请求
| 401 | Unauthorized | 该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息
| 403 | Forbidden | 不允许访问那个资源,该状态码表明对请求资源的访问被服务器拒绝了(权限,未授权IP等)
| 404 | Not Found | 服务器上没有请求的资源,路径错误等
| 409 | Conflict | 会导致一个或多个资源处于不一致状态
| 410 | Gone | 客户端请求的资源曾经存在,现在已经不存在了
| 500 | Internal Server Error | 内部资源出故障
| 503 | Service Unavailable | 服务器暂时处于超负载或正在停机维护,现在无法处理请求
ES6
Link:ECMAScript 6 入门
解构函数(Destructuring)
Link:变量的解构赋值
将一个数据结构分解为更小的部分的过程。
基本用法
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
默认值
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
暂时性死区(temporal dead zone,简称 TDZ)
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
对象的解构赋值
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
// 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
// foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
字符串的解构赋值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5
json 的解构赋值
let jsonData = {
id: 1,
name: 'tom',
age: 24,
data: [10, 100]
}
let { id, name, age, data } = jsonData
map
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
数据类型的新特性
includes(), startsWith(), endsWith()
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
padStart(),padEnd()
字符串补全长度的功能.
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
// 如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
'abc'.padStart(10, '0123456789')
// '0123456abc'
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
// padStart()的常见用途是为数值补全指定位数
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
trimStart(),trimEnd()
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
Map 对象扩展
Math.trunc()
// 用于去除一个数的小数部分,返回整数部分
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
Math.trunc('123.456') // 123
Math.trunc(true) //1
Math.trunc(false) // 0
Math.trunc(null) // 0
Math.trunc(NaN); // NaN
Math.trunc('foo'); // NaN
Math.trunc(); // NaN
Math.trunc(undefined) // NaN
Math.trunc = Math.trunc || function(x) {
return x < 0 ? Math.ceil(x) : Math.floor(x);
};
Math.sign()
// 用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值
/*
参数为正数,返回+1
参数为负数,返回-1
参数为 0,返回0
参数为-0,返回-0
其他值,返回NaN
*/
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
Math.sign('') // 0
Math.sign(true) // +1
Math.sign(false) // 0
Math.sign(null) // 0
Math.sign('9') // +1
Math.sign('foo') // NaN
Math.sign() // NaN
Math.sign(undefined) // NaN
Math.sign = Math.sign || function(x) {
x = +x; // convert to a number
if (x === 0 || isNaN(x)) {
return x;
}
return x > 0 ? 1 : -1;
}
指数运算符
2 ** 2 // 4
2 ** 3 // 8
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
函数
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null)
// 5 null
// 指定默认值后,length 属性将失真
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
// 尾递归
// 1
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
// 2
// function factorial(n, total) {
function factorial(n, total = 1) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
// factorial(5, 1) // 120
factorial(5) // 120
// 4
function tailFactorial(n, total) {
if (n === 1) return total;
return tailFactorial(n - 1, n * total);
}
function factorial(n) {
return tailFactorial(n, 1);
}
factorial(5) // 120
// 5 currying
function currying(fn, n) {
return function (m) {
return fn.call(this, m, n);
};
}
function tailFactorial(n, total) {
if (n === 1) return total;
return tailFactorial(n - 1, n * total);
}
const factorial = currying(tailFactorial, 1);
factorial(5) // 120
数组
// ...
function push(array, ...items) {
array.push(...items);
}
const arr = [
...(x > 0 ? ['a'] : []),
'b',
]
// 如果扩展运算符后面是一个空数组,则不产生任何效果
[...[], 1]
// [1]
// ES6 的写法
let arr1 = [0, 1, 2]
let arr2 = [3, 4, 5]
arr1.push(...arr2)
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]) // Sun Feb 01 2015 00:00:00 GMT+0800 (中国标准时间)
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
[...'hello']
// [ "h", "e", "l", "l", "o" ]
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
/**
* 关于 set 的一些说明及用法 start
*/
// 数组去重
const set = new Set([1, 2, 3, 4, 4]);
[...set] // [1, 2, 3, 4]
[...new Set('ababbc')].join('') // "abc"
/**
* end
*/
Array.from([1, 2, 3])
// [1, 2, 3]
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 将2号位到数组结束,复制到0号位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 对于没有部署 TypedArray 的 copyWithin 方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
// find 可以返回一个匹配的 Boolean 值,若一直为 false 返回为 undefined,若查到第一个 true 跳出循环
// 数组实例的 findIndex 方法的用法与 find 方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1
[NaN].indexOf(NaN) // -1
[NaN].findIndex(y => Object.is(NaN, y)) // 0
['a', 'b', 'c'].fill(7) // [7, 7, 7]
new Array(3).fill(7) // [7, 7, 7]
['a', 'b', 'c'].fill(7, 1, 2) // number start end
// ['a', 7, 'c']
let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr // [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]
let arr = new Array(3).fill([]);
arr[0].push(5);
arr // [[5], [5], [5]]
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[NaN].indexOf(NaN) // -1
[NaN].includes(NaN) // true
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
[1, 2, , 4, 5].flat()
// [1, 2, 4, 5]
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]
Array(3) // [, , ,]
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
// forEach方法
[,'a'].forEach((x, i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x === 'a') // true
// reduce方法
[1,,2].reduce((x, y) => x + y) // 3
// some方法
[,'a'].some(x => x !== 'a') // false
// map方法
[,'a'].map(x => 1) // [,1]
// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
[...['a',,'b']]
// [ "a", undefined, "b" ]
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
new Array(3).fill('a') // ["a","a","a"]
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
偏函数与柯里化
偏函数
在计算机科学中,局部应用是指固定一个函数的一些参数,然后产生另一个更小元的函数。
元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数。
柯里化
柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
// var f = curry(fn(a,b,c)) => function curried(nextArg)
// var fa = f(a) => function curried() prevArgs = [a]
// var fb = fa(b) => function curried() prevArgs = [a, b]
// var fc = fb(c) => fn(a,b,c)
// fc = fb(c)
// fc = fa(b)(c)
// fc = f(a)(b)(c) = fn(a,b,c)
function curry(fn, arity = fn.length) { // (fn, fn.length) fn.length => 传入参数的个数
// fn(a,b,c) 3
return (function nextCurried(prevArgs){
// [] => []
// [a]
// [a, b]
return function curried(...nextArg){ // nextArg = a
// nextArg => undefined
// a
// b prevArgs = [a]
// c prevArgs = [a, b]
var args = [ ...prevArgs, ...nextArg ]; // args = []
// []
// [a]
// [a, b]
// [a, b, c]
// args.length = 0
// args.length = 1
// args.length = 2
// args.length = 3
// arigty = 3
// arigty = 3
return args.length >= arity
? fn( ...args )
: nextCurried( args )
// if (args.length >= arity) { // arr.length >= fn.length => 数组长度大于等于参数的个数 => 解构函数
// return fn( ...args ); // 返回整个arr
// }
// else {
// return nextCurried( args ); // 重新走一次第二步柯里化函数
// }
};
})( [] );
}
笔记 Learning Notes Work Notes vue 华夏
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!