首页

vue传值方式总结 (十二种方法)

前端达人

一.父传子传递

(1)在父组件的子组件标签上绑定一个属性,挂载要传输的变量
(2)在子组件中通过props来接受数据,props可以是数组也可以是对象,接受的数据可以直接使用 props: [“属性 名”] props:{属性名:数据类型}
代码示例:

//父组件
<template>
  <div>
    <i>父组件</i>
    <!--页面使用-->
    <son :data='name'></son> 
  </div>
</template>

<script>
import son from "./son.vue";//导入父组件
export default {
  components: { son },//注册组件
  name: "父组件",
  data() {
    return {
      name: "Frazier", //父组件定义变量
    };
  },
};
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
//子组件
<template>
  <div>{{data}}</div>
</template>

<script>
export default {
components: { },
  name: '子组件',
  props:["data"],
};
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

二.子传父传递

(1)在父组件的子组件标签上自定义一个事件,然后调用需要的方法
(2)在子组件的方法中通过 this.$emit(“事件”)来触发在父组件中定义的事件,数据是以参数的形式进行传递的
代码示例:

//父组件
<template>
  <div>
    <i>父组件</i>
    <!--页面使用-->
    <son @lcclick="lcclick"></son>//自定义一个事件
  </div>
</template>

<script>
import son from "./son.vue"; //导入父组件
export default {
  components: { son }, //注册组件
  name: "父组件",
  data() {
    return {};
  },
  methods: {
    lcclick(){
      alert('子传父')
    }
  },
};
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
//子组件
<template>
  <div>
    <button @click="lcalter">点我</button>
  </div>
</template>

<script>
export default {
components: { },
  name: '子组件',
  methods: {
    lcalter(){
      this.$emit('lcclick')//通过emit来触发事件
    }
  },
};
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

三.兄弟组件通信(bus总线)

(1)在src中新建一个Bus.js的文件,然后导出一个空的vue实例
(2)在传输数据的一方引入Bus.js 然后通过Bus.e m i t ( “ 事 件 名 ” , " 参 数 " ) 来 来 派 发 事 件 , 数 据 是 以 emit(“事件名”,"参数")来来派发事件,数据是以emit(,"")emit()的参 数形式来传递
(3)在接受的数据的一方 引入 Bus.js 然后通过 Bus.$on(“事件名”,(data)=>{data是接受的数据})
图片示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四.ref/refs(父子组件通信)

(1)ref 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,
(2)可以通过实例直接调用组件的方法或访问数据。也算是子组件向父组件传值的一种
代码示例:

//父组件
<template>
  <div>
    <button @click="sayHello">sayHello</button>
    <child ref="childForRef"></child>
  </div>
</template>
<script>
import child from './child.vue'
  export default {
    components: { child },
    data () {
      return {
        childForRef: null,
      }
    },
    mounted() {
      this.childForRef = this.$refs.childForRef;
      console.log(this.childForRef.name);
    },
    methods: {
      sayHello() {
        this.childForRef.sayHello()
      }
    }
  }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
//子组件
<template>
  <div>child 的内容</div>
</template>
<script>
export default {
  data () {
    return {
      name: '我是 child',
    }
  },
  methods: {
    sayHello () {
      console.log('hello');
      alert('hello');
    }
  }
}
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

五.Vuex通信

组件通过 dispatch 到 actions,actions 是异步操作,再 actions中通过 commit 到 mutations,mutations 再通过逻辑操作改变 state,从而同步到组件,更新其数据状态
代码示例:

//父组件
template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>
<script>
  import ChildA from './ChildA' // 导入A组件
  import ChildB from './ChildB' // 导入B组件
  export default {
    components: {ChildA, ChildB} // 注册组件
  }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//子组件A
<template>
 <div id="childA">
   <h1>我是A组件</h1>
   <button @click="transform">点我让B组件接收到数据</button>
   <p>因为点了B,所以信息发生了变化:{{BMessage}}</p>
 </div>
</template>
<script>
 export default {
   data() {
     return {
       AMessage: 'Hello,B组件,我是A组件'
     }
   },
   computed: {
     BMessage() {
       // 这里存储从store里获取的B组件的数据
       return this.$store.state.BMsg
     }
   },
   methods: {
     transform() {
       // 触发receiveAMsg,将A组件的数据存放到store里去
       this.$store.commit('receiveAMsg', {
         AMsg: this.AMessage
       })
     }
   }
 }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
//子组件B
<template>
 <div id="childB">
   <h1>我是B组件</h1>
   <button @click="transform">点我让A组件接收到数据</button>
   <p>点了A,我的信息发生了变化:{{AMessage}}</p>
 </div>
</template>

<script>
 export default {
   data() {
     return {
       BMessage: 'Hello,A组件,我是B组件'
     }
   },
   computed: {
     AMessage() {
       // 这里存储从store里获取的A组件的数据
       return this.$store.state.AMsg
     }
   },
   methods: {
     transform() {
       // 触发receiveBMsg,将B组件的数据存放到store里去
       this.$store.commit('receiveBMsg', {
         BMsg: this.BMessage
       })
     }
   }
 }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
//vuex
import Vue from 'vue'
 import Vuex from 'vuex'
 Vue.use(Vuex)
 const state = {
   AMsg: '',
   BMsg: ''
 }

 const mutations = {
   receiveAMsg(state, payload) {
     // 将A组件的数据存放于state
     state.AMsg = payload.AMsg
   },
   receiveBMsg(state, payload) {
     // 将B组件的数据存放于state
     state.BMsg = payload.BMsg
   }
 }

 export default new Vuex.Store({
   state,
   mutations
 }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

六.$parent

通过parent可以获父组件实例 ,然 后通过这个实例就可以访问父组件的属 性和方法 ,它还有一个兄弟parent可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟parent可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟root,可以获取根组件实例。
代码示例:

// 获父组件的数据
this.$parent.foo

// 写入父组件的数据
this.$parent.foo = 2

// 访问父组件的计算属性
this.$parent.bar

// 调用父组件的方法
this.$parent.baz()

//在子组件传给父组件例子中,可以使用this.$parent.getNum(100)传值给父组件。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

七.sessionStorage传值

sessionStorage 是浏览器的全局对象,存在它里面的数据会在页面关闭时清除 。运用这个特性,我们可以在所有页面共享一份数据。
代码示例:

// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');

// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');

// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');

// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear(); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意:里面存的是键值对,只能是字符串类型,如果要存对象的话,需要使用 let objStr = JSON.stringify(obj) 转成字符串然后再存储(使用的时候 let obj = JSON.parse(objStr) 解析为对象)。
推荐一个库 good-storage ,它封装了sessionStorage ,可以直接用它的API存对象

//localStorage
 storage.set(key,val) 
 storage.get(key, def)
//sessionStorage
 storage.session.set(key, val)
 storage.session.get(key, val) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

八.路由传值

使用问号传值
A页面跳转B页面时使用 this.r o u t e r . p u s h ( ’ / B ? n a m e = d a n s e e k ’ ) B 页 面 可 以 使 用 t h i s . router.push(’/B?name=danseek’) B页面可以使用 this.router.push(/B?name=danseek)B使this.route.query.name 来获取A页面传过来的值
上面要注意router和route的区别
使用冒号传值
配置如下路由:

{
    path: '/b/:name',
    name: 'b',
    component: () => import( '../views/B.vue')
 }, 
  • 1
  • 2
  • 3
  • 4
  • 5

在B页面可以通过 this.$route.params.name 来获取路由传入的name的值

使用父子组件传值
由于router-view本身也是一个组件,所以我们也可以使用父子组件传值方式传值,然后在对应的子页面里加上props,因为type更新后没有刷新路由,所以不能直接在子页面的mounted钩子里直接获取最新type的值,而要使用watch

<router-view :type="type"></router-view>

// 子页面
props: ['type']
watch: {
       type(){
           // console.log("在这个方法可以时刻获取最新的数据:type=",this.type)
       },
}, 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

九.祖传孙 $attrs

正常情况下需要借助父亲的props作为中间过渡,但是这样在父亲组件就会多了一些跟父组件业务无关的属性,耦合度高,借助$attrs可以简化些,而且祖跟孙都无需做修改
祖组件:

<template>
    <section>
        <parent name="grandParent" sex="男" age="88" hobby="code" @sayKnow="sayKnow"></parent>
    </section>
</template>

<script>
    import Parent from './Parent'
    export default {
        name: "GrandParent",
        components: {
          Parent
        },
        data() {
            return {}
        },
        methods: {
          sayKnow(val){
            console.log(val)
          }
        },
        mounted() {
        }
    }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

父组件

template>
  <section>
    <p>父组件收到</p>
    <p>祖父的名字:{{name}}</p>
    <children v-bind="$attrs" v-on="$listeners"></children>
  </section>
</template>

<script>
  import Children from './Children'

  export default {
    name: "Parent",
    components: {
      Children
    },
    // 父组件接收了name,所以name值是不会传到子组件的
    props:['name'],
    data() {
      return {}
    },
    methods: {},
    mounted() {
    }
  }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

子组件

<template>
  <section>
    <p>子组件收到</p>
    <p>祖父的名字:{{name}}</p>
    <p>祖父的性别:{{sex}}</p>
    <p>祖父的年龄:{{age}}</p>
    <p>祖父的爱好:{{hobby}}</p>

    <button @click="sayKnow">我知道啦</button>
  </section>
</template>

<script>
  export default {
    name: "Children",
    components: {},
    // 由于父组件已经接收了name属性,所以name不会传到子组件了
    props:['sex','age','hobby','name'],
    data() {
      return {}
    },
    methods: {
      sayKnow(){
        this.$emit('sayKnow','我知道啦')
      }
    },
    mounted() {
    }
  }
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

十.孙传祖使用$listeners

文字内容同第九个

祖组件

<template>
  <div id="app">
    <children-one @eventOne="eventOne"></children-one>
    {{ msg }}
  </div>
</template>
<script>
import ChildrenOne from '../src/components/children.vue'
export default {
  name: 'App',
  components: {
    ChildrenOne,
  },
  data() {
    return {
      msg: ''
    }
  },
  methods: {
    eventOne(value) {
      this.msg = value
    }
  }
}
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

父组件

<template>
  <div>
    <children-two v-on="$listeners"></children-two>
  </div>
</template>

<script>
import ChildrenTwo from './childrenTwo.vue'

export default {
  name: 'childrenOne',
  components: {
    ChildrenTwo
  }
}
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

子组件

<template>
  <div>
    <button @click="setMsg">点击传给祖父</button>
  </div>
</template>

<script>
export default {
  name: 'children',
  methods: {
    setMsg() {
      this.$emit('eventOne', '123')
    }
  }
}
</script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

十一.promise传参

promise 中 resolve 如何传递多个参数

//类似与这样使用,但实际上后面两个参数无法获取
promise = new Promise((resolve,reject)=>{
    let a = 1
    let b = 2
    let c = 3
    resolve(a,b,c) 
})
promise.then((a,b,c)=>{
    console.log(a,b,c)
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

resolve() 只能接受并处理一个参数,多余的参数会被忽略掉。
如果想多个用数组,或者对象方式。。
数组

promise = new Promise((resolve,reject)=>{
    resolve([1,2,3]) 
})
promise.then((arr)=>{
    console.log(arr[0],arr[1],arr[2])
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对象

promise = new Promise((resolve,reject)=>{
    resolve({a:1,b:2,c:3}) 
})
promise.then(obj=>{
    console.log(obj.a,obj.b,obj.c)
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

十二.全局变量

定义一个全局变量,在有值的组件直接赋值,在需要的组件内直接使用就可以了。


转自:csdn 作者:Frazier_梁超


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

一口(很长的)气了解 babel

前端达人

最近几年,如果你是一名前端开发者,如果你没有使用甚至听说过 babel,可能会被当做穿越者吧?

说到 babel,一连串名词会蹦出来:

  • babel-cli
  • babel-core
  • babel-runtime
  • babel-node
  • babel-polyfill
  • ...

这些都是 babel 吗?他们分别是做什么的?有区别吗?

babel 到底做了什么?怎么做的?

简单来说把 JavaScript 中 es2015/2016/2017/2046 的新语法转化为 es5,让低端运行环境(如浏览器和 node )能够认识并执行。本文以 babel 6.x 为基准进行讨论。最近 babel 出了 7.x,放在最后聊。

严格来说,babel 也可以转化为更低的规范。但以目前情况来说,es5 规范已经足以覆盖绝大部分浏览器,因此常规来说转到 es5 是一个安全且流行的做法。

如果你对 es5/es2015 等等也不了解的话,那你可能真的需要先补补课了。

使用方法

总共存在三种方式:

  1. 使用单体文件 (standalone script)
  2. 命令行 (cli)
  3. 构建工具的插件 (webpack 的 babel-loader, rollup 的 rollup-plugin-babel)。

其中后面两种比较常见。第二种多见于 package.json 中的 scripts 段落中的某条命令;第三种就直接集成到构建工具中。

这三种方式只有入口不同而已,调用的 babel 内核,处理方式都是一样的,所以我们先不纠结入口的问题。

运行方式和插件

babel 总共分为三个阶段:解析,转换,生成。

babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的。

插件总共分为两种:

  • 当我们添加 语法插件 之后,在解析这一步就使得 babel 能够解析更多的语法。(顺带一提,babel 内部使用的解析类库叫做 babylon,并非 babel 自行开发)

举个简单的例子,当我们定义或者调用方法时,最后一个参数之后是不允许增加逗号的,如 callFoo(param1, param2,) 就是非法的。如果源码是这种写法,经过 babel 之后就会提示语法错误。

但最近的 JS 提案中已经允许了这种新的写法(让代码 diff 更加清晰)。为了避免 babel 报错,就需要增加语法插件 babel-plugin-syntax-trailing-function-commas

  • 当我们添加 转译插件 之后,在转换这一步把源码转换并输出。这也是我们使用 babel 最本质的需求。

比起语法插件,转译插件其实更好理解,比如箭头函数 (a) => a 就会转化为 function (a) {return a}。完成这个工作的插件叫做 babel-plugin-transform-es2015-arrow-functions

同一类语法可能同时存在语法插件版本和转译插件版本。如果我们使用了转译插件,就不用再使用语法插件了。

配置文件

既然插件是 babel 的根本,那如何使用呢?总共分为 2 个步骤:

  1. 将插件的名字增加到配置文件中 (根目录下创建 .babelrc 或者 package.json 的 babel 里面,格式相同)
  2. 使用 npm install babel-plugin-xxx 进行安装

具体书写格式就不详述了。

preset

比如 es2015 是一套规范,包含大概十几二十个转译插件。如果每次要开发者一个个添加并安装,配置文件很长不说,npm install 的时间也会很长,更不谈我们可能还要同时使用其他规范呢。

为了解决这个问题,babel 还提供了一组插件的集合。因为常用,所以不必重复定义 & 安装。(单点和套餐的差别,套餐省下了巨多的时间和配置的精力)

preset 分为以下几种:

  • 官方内容,目前包括 env, react, flow, minify 等。这里最重要的是 env,后面会详细介绍。
  • stage-x,这里面包含的都是当年最新规范的草案,每年更新。
    这里面还细分为
    • Stage 0 - 稻草人: 只是一个想法,经过 TC39 成员提出即可。
    • Stage 1 - 提案: 初步尝试。
    • Stage 2 - 初稿: 完成初步规范。
    • Stage 3 - 候选: 完成规范和浏览器初步实现。
    • Stage 4 - 完成: 将被添加到下一年度发布。

例如 syntax-dynamic-import 就是 stage-2 的内容,transform-object-rest-spread 就是 stage-3 的内容。
此外,低一级的 stage 会包含所有高级 stage 的内容,例如 stage-1 会包含 stage-2, stage-3 的所有内容。
stage-4 在下一年更新会直接放到 env 中,所以没有单独的 stage-4 可供使用。

  • es201x, latest
    这些是已经纳入到标准规范的语法。例如 es2015 包含 arrow-functions,es2017 包含 syntax-trailing-function-commas。但因为 env 的出现,使得 es2016 和 es2017 都已经废弃。所以我们经常可以看到 es2015 被单独列出来,但极少看到其他两个。
    latest 是 env 的雏形,它是一个每年更新的 preset,目的是包含所有 es201x。但也是因为更加灵活的 env 的出现,已经废弃。

执行顺序

很简单的几条原则:

  • Plugin 会运行在 Preset 之前。
  • Plugin 会从前到后顺序执行。
  • Preset 的顺序则 刚好相反(从后向前)。

preset 的逆向顺序主要是为了保证向后兼容,因为大多数用户的编写顺序是 ['es2015', 'stage-0']。这样必须先执行 stage-0 才能确保 babel 不报错。因此我们编排 preset 的时候,也要注意顺序,其实只要按照规范的时间顺序列出即可。

插件和 preset 的配置项

简略情况下,插件和 preset 只要列出字符串格式的名字即可。但如果某个 preset 或者插件需要一些配置项(或者说参数),就需要把自己先变成数组。第一个元素依然是字符串,表示自己的名字;第二个元素是一个对象,即配置对象。

最需要配置的当属 env,如下:

"presets": [ // 带了配置项,自己变成数组  [ // 第一个元素依然是名字  "env", // 第二个元素是对象,列出配置项  { "module": false } ], // 不带配置项,直接列出名字  "stage-2" ] 

env (重点)

因为 env 最为常用也最重要,所以我们有必要重点关注。

env 的核心目的是通过配置得知目标环境的特点,然后只做必要的转换。例如目标浏览器支持 es2015,那么 es2015 这个 preset 其实是不需要的,于是代码就可以小一点(一般转化后的代码总是更长),构建时间也可以缩短一些。

如果不写任何配置项,env 等价于 latest,也等价于 es2015 + es2016 + es2017 三个相加(不包含 stage-x 中的插件)。env 包含的插件列表维护在这里

下面列出几种比较常用的配置方法:

{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }] ] }

如上配置将考虑所有浏览器的最新2个版本(safari大于等于7.0的版本)的特性,将必要的代码进行转换。而这些版本已有的功能就不进行转化了。这里的语法可以参考 browserslist

{ "presets": [ ["env", { "targets": { "node": "6.10" } }] ] }

如上配置将目标设置为 nodejs,并且支持 6.10 及以上的版本。也可以使用 node: 'current' 来支持最新稳定版本。例如箭头函数在 nodejs 6 及以上将不被转化,但如果是 nodejs 0.12 就会被转化了。

另外一个有用的配置项是 modules。它的取值可以是 amdumdsystemjscommonjs 和 false。这可以让 babel 以特定的模块化格式来输出代码。如果选择 false 就不进行模块化处理。

其他配套工具

以上讨论了 babel 的核心处理机制和配置方法等,不论任何入口调用 babel 都走这一套。但文章开头提的那一堆 babel-* 还是让人一头雾水。实际上这些 babel-* 大多是不同的入口(方式)来使用 babel,下面来简单介绍一下。

babel-cli

顾名思义,cli 就是命令行工具。安装了 babel-cli 就能够在命令行中使用 babel 命令来编译文件。

在开发 npm package 时经常会使用如下模式:

  • 把 babel-cli 安装为 devDependencies
  • 在 package.json 中添加 scripts (比如 prepublish),使用 babel 命令编译文件
  • npm publish

这样既可以使用较新规范的 JS 语法编写源码,同时又能支持旧版环境。因为项目可能不太大,用不到构建工具 (webpack 或者 rollup),于是在发布之前用 babel-cli 进行处理。

babel-node

babel-node 是 babel-cli 的一部分,它不需要单独安装。

它的作用是在 node 环境中,直接运行 es2015 的代码,而不需要额外进行转码。例如我们有一个 js 文件以 es2015 的语法进行编写(如使用了箭头函数)。我们可以直接使用 babel-node es2015.js 进行执行,而不用再进行转码了。

可以说:babel-node = babel-polyfill + babel-register。那这两位又是谁呢?

babel-register

babel-register 模块改写 require 命令,为它加上一个钩子。此后,每当使用 require 加载 .js.jsx.es 和 .es6 后缀名的文件,就会先用 babel 进行转码。

使用时,必须首先加载 require('babel-register')

需要注意的是,babel-register 只会对 require 命令加载的文件转码,而 不会对当前文件转码

另外,由于它是实时转码,所以 只适合在开发环境使用

babel-polyfill

babel 默认只转换 js 语法,而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。

举例来说,es2015 在 Array 对象上新增了 Array.from 方法。babel 就不会转码这个方法。如果想让这个方法运行,必须使用 babel-polyfill。(内部集成了 core-js 和 regenerator)

使用时,在所有代码运行之前增加 require('babel-polyfill')。或者更常规的操作是在 webpack.config.js 中将 babel-polyfill 作为第一个 entry。因此必须把 babel-polyfill 作为 dependencies 而不是 devDependencies

babel-polyfill 主要有两个缺点:

  1. 使用 babel-polyfill 会导致打出来的包非常大,因为 babel-polyfill 是一个整体,把所有方法都加到原型链上。比如我们只使用了 Array.from,但它把 Object.defineProperty 也给加上了,这就是一种浪费了。这个问题可以通过单独使用 core-js 的某个类库来解决,core-js 都是分开的。
  2. babel-polyfill 会污染全局变量,给很多类的原型链上都作了修改,如果我们开发的也是一个类库供其他开发者使用,这种情况就会变得非常不可控。

因此在实际使用中,如果我们无法忍受这两个缺点(尤其是第二个),通常我们会倾向于使用 babel-plugin-transform-runtime

但如果代码中包含高版本 js 中类型的实例方法 (例如 [1,2,3].includes(1)),这还是要使用 polyfill。

babel-runtime 和 babel-plugin-transform-runtime (重点)

我们时常在项目中看到 .babelrc 中使用 babel-plugin-transform-runtime,而 package.json 中的 dependencies (注意不是 devDependencies) 又包含了 babel-runtime,那这两个是不是成套使用的呢?他们又起什么作用呢?

先说 babel-plugin-transform-runtime

babel 会转换 js 语法,之前已经提过了。以 async/await 举例,如果不使用这个 plugin (即默认情况),转换后的代码大概是:

// babel 添加一个方法,把 async 转化为 generator function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段  // 具体使用处 var _ref = _asyncToGenerator(function* (arg1, arg2) { yield (0, something)(arg1, arg2); }); 

不用过于纠结具体的语法,只需看到,这个 _asyncToGenerator 在当前文件被定义,然后被使用了,以替换源代码的 await。但每个被转化的文件都会插入一段 _asyncToGenerator 这就导致重复和浪费了。

在使用了 babel-plugin-transform-runtime 了之后,转化后的代码会变成

// 从直接定义改为引用,这样就不会重复定义了。 var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); // 具体使用处是一样的 var _ref = _asyncToGenerator3(function* (arg1, arg2) { yield (0, something)(arg1, arg2); }); 

从定义方法改成引用,那重复定义就变成了重复引用,就不存在代码重复的问题了。

但在这里,我们也发现 babel-runtime 出场了,它就是这些方法的集合处,也因此,在使用 babel-plugin-transform-runtime 的时候必须把 babel-runtime 当做依赖。

再说 babel-runtime,它内部集成了

  1. core-js: 转换一些内置类 (PromiseSymbols等等) 和静态方法 (Array.from 等)。绝大部分转换是这里做的。自动引入。
  2. regenerator: 作为 core-js 的拾遗补漏,主要是 generator/yield 和 async/await 两组的支持。当代码中有使用 generators/async 时自动引入。
  3. helpers, 如上面的 asyncToGenerator 就是其中之一,其他还有如 jsxclassCallCheck 等等,可以查看 babel-helpers。在代码中有内置的 helpers 使用时(如上面的第一段代码)移除定义,并插入引用(于是就变成了第二段代码)。

babel-plugin-transform-runtime 不支持 实例方法 (例如 [1,2,3].includes(1))

此外补充一点,把 helpers 抽离并统一起来,避免重复代码的工作还有一个 plugin 也能做,叫做 babel-plugin-external-helpers。但因为我们使用的 transform-runtime 已经包含了这个功能,因此不必重复使用。而且 babel 的作者们也已经开始讨论这两个插件过于类似,正在讨论在 babel 7 中把 external-helpers 删除,讨论在 issue#5699 中。

babel-loader

前面提过 babel 的三种使用方法,并且已经介绍过了 babel-cli。但一些大型的项目都会有构建工具 (如 webpack 或 rollup) 来进行代码构建和压缩 (uglify)。理论上来说,我们也可以对压缩后的代码进行 babel 处理,但那会非常慢。因此如果在 uglify 之前就加入 babel 处理,岂不完美?

所以就有了 babel 插入到构建工具内部这样的需求。以(我还算熟悉的) webpack 为例,webpack 有 loader 的概念,因此就出现了 babel-loader

和 babel-cli 一样,babel-loader 也会读取 .babelrc 或者 package.json 中的 babel 段作为自己的配置,之后的内核处理也是相同。唯一比 babel-cli 复杂的是,它需要和 webpack 交互,因此需要在 webpack 这边进行配置。比较常见的如下:

module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader' } ] } 

如果想在这里传入 babel 的配置项,也可以把改成:

// loader: 'babel-loader' 改成如下: use: { loader: 'babel-loader', options: { // 配置项在这里  } } 

这里的配置项优先级是最高的。但我认为放到单独的配置文件中更加清晰合理,可读性强一些。

小结一下

Babel 7.x

最近 babel 发布了 7.0。因为上面部分都是针对 6.x 编写的,所以我们关注一下 7.0 带来的变化(核心机制方面没有变化,插件,preset,解析转译生成这些都没有变化)

我只挑选一些和开发者关系比较大的列在这里,省略的多数是针对某一个 plugin 的改动。完整的列表可以参考官网

preset 的变更:淘汰 es201x,删除 stage-x,强推 env (重点)

淘汰 es201x 的目的是把选择环境的工作交给 env 自动进行,而不需要开发者投入精力。凡是使用 es201x 的开发者,都应当使用 env 进行替换。但这里的淘汰 (原文 deprecated) 并不是删除,只是不推荐使用了,不好说 babel 8 就真的删了。

与之相比,stage-x 就没那么好运了,它们直接被删了。这是因为 babel 团队认为为这些 “不稳定的草案” 花费精力去更新 preset 相当浪费。stage-x 虽然删除了,但它包含的插件并没有删除(只是被更名了,可以看下面一节),我们依然可以显式地声明这些插件来获得等价的效果。完整列表

为了减少开发者替换配置文件的机械工作,babel 开发了一款 babel-upgrade 的工具,它会检测 babel 配置中的 stage-x 并且替换成对应的 plugins。除此之外它还有其他功能,我们一会儿再详细看。(总之目的就是让你更加平滑地迁移到 babel 7)

npm package 名称的变化 (重点)

这是 babel 7 的一个重大变化,把所有 babel-* 重命名为 @babel/*,例如:

  1. babel-cli 变成了 @babel/cli
  2. babel-preset-env 变成了 @babel/preset-env。进一步,还可以省略 preset 而简写为 @babel/env
  3. babel-plugin-transform-arrow-functions 变成了 @babel/plugin-transform-arrow-functions。和 preset 一样,plugin 也可以省略,于是简写为 @babel/transform-arrow-functions

这个变化不单单应用于 package.json 的依赖中,包括 .babelrc 的配置 (pluginspresets) 也要这么写,为了保持一致。例如

{
  "presets": [ -   "env" +   "@babel/preset-env"  ]
} 

顺带提一句,上面提过的 babel 解析语法的内核 babylon 现在重命名为 @babel/parser,看起来是被收编了。

上文提过的 stage-x 被删除了,它包含的插件虽然保留,但也被重命名了。babel 团队希望更明显地区分已经位于规范中的插件 (如 es2015 的 babel-plugin-transform-arrow-functions) 和仅仅位于草案中的插件 (如 stage-0 的 @babel/plugin-proposal-function-bind)。方式就是在名字中增加 proposal,所有包含在 stage-x 的转译插件都使用了这个前缀,语法插件不在其列。

最后,如果插件名称中包含了规范名称 (-es2015--es3- 之类的),一律删除。例如 babel-plugin-transform-es2015-classes 变成了 @babel/plugin-transform-classes。(这个插件我自己没有单独用过,惭愧)

不再支持低版本 node

babel 7.0 开始不再支持 nodejs 0.10, 0.12, 4, 5 这四个版本,相当于要求 nodejs >= 6 (当前 nodejs LTS 是 8,要求也不算太过分吧)。

这里的不再支持,指的是在这些低版本 node 环境中不能使用 babel 转译代码,但 babel 转译后的代码依然能在这些环境上运行,这点不要混淆。

only 和 ignore 匹配规则的变化

在 babel 6 时,ignore 选项如果包含 *.foo.js,实际上的含义 (转化为 glob) 是 ./**/*.foo.js,也就是当前目录 包括子目录 的所有 foo.js 结尾的文件。这可能和开发者常规的认识有悖。

于是在 babel 7,相同的表达式 *.foo.js 只作用于当前目录,不作用于子目录。如果依然想作用于子目录的,就要按照 glob 的完整规范书写为 ./**/*.foo.js 才可以。only 也是相同。

这个规则变化只作用于通配符,不作用于路径。所以 node_modules 依然包含所有它的子目录,而不单单只有一层。(否则全世界开发者都要爆炸)

@babel/node 从 @babel/cli 中独立了

和 babel 6 不同,如果要使用 @babel/node,就必须单独安装,并添加到依赖中。

babel-upgrade

在提到删除 stage-x 时候提过这个工具,它的目的是帮助用户自动化地从 babel 6 升级到 7。

这款升级工具的功能包括:(这里并不列出完整列表,只列出比较重要和常用的内容)

  1. package.json
  2. 把依赖(和开发依赖)中所有的 babel-* 替换为 @babel/*
  3. 把这些 @babel/* 依赖的版本更新为最新版 (例如 ^7.0.0)
  4. 如果 scripts 中有使用 babel-node,自动添加 @babel/node 为开发依赖
  5. 如果有 babel 配置项,检查其中的 plugins 和 presets,把短名 (env) 替换为完整的名字 (@babel/preset-env)
  6. .babelrc
  7. 检查其中的 plugins 和 presets,把短名 (env) 替换为完整的名字 (@babel/preset-env)
  8. 检查是否包含 preset-stage-x,如有替换为对应的插件并添加到 plugins

使用方式如下:

# 不安装到本地而是直接运行命令,npm 的新功能 npx babel-upgrade --write # 或者常规方式 npm i babel-upgrade -g
babel-upgrade --write

babel-upgrade 工具本身也还在开发中,还列出了许多 TODO 没有完成,因此之后的功能可能会更加丰富,例如上面提过的 ignore 的通配符转化等等。


转自:知乎。作者:前端解忧杂货铺


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

TypeScript VS JavaScript 深度对比

前端达人



TypeScript 和 JavaScript 是目前项目开发中较为流行的两种脚本语言,我们已经熟知 TypeScript 是 JavaScript 的一个超集,但是 TypeScript 与 JavaScript 之间又有什么样的区别呢?在选择开发语言时,又该如何抉择呢?

本文将会深入对比这两种语言,讨论两种语言之间的关联和差异,并概述两种语言各自的优势。

JavaScript 和 TypeScript 的概要介绍

JavaScript

JavaScript 是一种轻量级的解释性脚本语言,可嵌入到 HTML 页面中,在浏览器端执行,能够实现浏览器端丰富的交互功能,为用户带来流畅多样的用户体验。

JavaScript 是基于对象和事件驱动的,无需特定的语言环境,只需在支持的浏览器上就能运行。

JavaScript 语言具有以下特点:

  • JavaScript 是一种脚本编写语言,无需编译,只要嵌入 HTML 代码中,就能由浏览器逐行加载解释执行。

  • JavaScript 是一种基于对象的语言,可以创建对象同时使用现有对象。但是 Javascript 并不支持其它面向对象语言所具有的继承和重载功能。

  • JavaScript 的语法简单,使用的变量为弱类型。

  • JavaScript 语言较为安全,仅在浏览器端执行,不会访问本地硬盘数据。

  • JavaScript 语言具有动态性。JavaScript 是事件驱动的,只根据用户的操作做出相应的反应处理。

  • JavaScript 只依赖于浏览器,与操作系统的因素无关。因此 JavaScript 是一种跨平台的语言。

  • JavaScript 兼容性较好,能够与其他技术(如 XML,REST API 等)一起使用。


  • TypeScript

    TypeScript 是 Microsoft 开发和维护的一种面向对象的编程语言。它是 JavaScript 的超集,包含了 JavaScript 的所有元素,可以载入 JavaScript 代码运行,并扩展了 JavaScript 的语法。

    TypeScript 具有以下特点:

  • TypeScript 是 Microsoft 推出的开源语言,使用 Apache 授权协议

  • TypeScript 增加了静态类型、类、模块、接口和类型注解

  • TypeScript 可用于开发大型的应用

  • TypeScript 易学易于理解


  • JavaScript 和 TypeScript 的主要差异

    TypeScript 可以使用 JavaScript 中的所有代码和编码概念,TypeScript 是为了使 JavaScript 的开发变得更加容易而创建的。例如,TypeScript 使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序

  • TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。

  • JavaScript 代码可以在无需任何修改的情况下与 TypeScript 一同工作,同时可以使用编译器将 TypeScript 代码转换为 JavaScript。

  • TypeScript 通过类型注解提供编译时的静态类型检查。

  • TypeScript 中的数据要求带有明确的类型,JavaScript不要求。

  • TypeScript 为函数提供了缺省参数值。

  • TypeScript 引入了 JavaScript 中没有的“类”概念。

  • TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。


  • TypeScript 的优势

    下面列举 TypeScript 相比于 JavaScript 的显著优势:

    1. 静态输入

    静态类型化是一种功能,可以在开发人员编写脚本时检测错误。查找并修复错误是当今开发团队的迫切需求。有了这项功能,就会允许开发人员编写更健壮的代码并对其进行维护,以便使得代码质量更好、更清晰。

    2. 大型的开发项目

    有时为了改进开发项目,需要对代码库进行小的增量更改。这些小小的变化可能会产生严重的、意想不到的后果,因此有必要撤销这些变化。使用TypeScript工具来进行重构更变的容易、快捷。

    3. 更好的协作

    当发开大型项目时,会有许多开发人员,此时乱码和错误的机也会增加。类型安全是一种在编码期间检测错误的功能,而不是在编译项目时检测错误。这为开发团队创建了一个更高效的编码和调试过程。

    4. 更强的生产力

    干净的 ECMAScript 6 代码,自动完成和动态输入等因素有助于提高开发人员的工作效率。这些功能也有助于编译器创建优化的代码。

    JavaScript 的优势

    相比于 TypeScript,JavaScript 也有一些明显优势。

    1. 人气

    JavaScript 的开发者社区仍然是巨大而活跃的,在社区中可以很方便地找到大量成熟的开发项目和可用资源。

    2. 学习曲线

    由于 JavaScript 语言发展的较早,也较为成熟,所以仍有一大批开发人员坚持使用他们熟悉的脚本语言 JavaScript,而不是学习 TypeScript。

    3. 本地浏览器支持

    TypeScript 代码需要被编译(输出 JavaScript 代码),这是 TypeScript 代码执行时的一个额外的步骤。

    4. 不需要注释

    为了充分利用 TypeScript 特性,开发人员需要不断注释他们的代码,这可能会使项目效率降低。

    5. 灵活性

    有些开发人员更喜欢 JavaScript 的灵活性。

    如何抉择

    TypeScript 正在成为开发大型编码项目的有力工具。因为其面向对象编程语言的结构保持了代码的清洁、一致和简单的调试。因此在应对大型开发项目时,使用 TypeScript 更加合适。如果有一个相对较小的编码项目,似乎没有必要使用 TypeScript,只需使用灵活的 JavaScript 即可。


    作者:grain先森
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。






    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服

    this解析 记录分享

    前端达人

    this指向的问题也是JavaScript中的难点之一了,也是面试常问的问题,很多时候对this指向的问题就很懵逼,明明应该是指向他,为什么又指向他了…所以我就学习了一下这方面的知识,整理了一下,希望能够帮助大家

    为什么要用this?

    首先看一段代码

    function identify(){
        return this.name.toUpperCase()
    }
    function speak(){
        var greeting = '你好,我是'+identify.call(this)
        console.log(greeting)
    }
    
    var me ={
        name:'kyle'
    }
    
    var you ={
        name:"reader"
    }
    identify.call(me) 
    identify.call(you) 
    
    speak.call(me) //?  你好,我是KYLE
    speak.call(you) //? 你好,我是READER 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    上面的这段代码中可以从不同的上下文对象 me 和 you 中重复的使用identify函数和speak函数
    如果你不使用this的话 你就要显式的将上下文对象作为参数传递进去,比如这样:

    function identify(context){
        return context.name.toUpperCase()
    }
    function speak(context){
        var greeting = '你好,我是'+identify(context)
        console.log(greeting)
    }
    
    var me ={
        name:'kyle'
    }
    
    var you ={
        name:"reader"
    }
    identify(me)
    identify(you) 
    
    speak(me) 
    speak(you) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    就像这样,这样看起来就不想上面那样简洁了,你要把一个对象传来传去的

    认识this

    刚见到this的时候 觉得this指向是这个函数自身,或者是函数的作用域,后来发现其实不是这样的的,不过也不能说错了,因为有些情况确实是这样的,比如这样:

    function foo(num){
        console.log('foo'+ num)
        this.count ++ 
    }
    foo.count = 0
    
    var i;
    for(i = 0;i<10;i++){
        if(i>5){
            foo.call(foo,i)
        }
    }
    console.log(foo.count) //4 这样的话 this指向了foo本身  foo上面的count属性++ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    无法指向函数作用域

    var a = 3
    function foo() {
        var a = 2;
        bar.call(foo);
    }
    function bar() {
        console.log( this.a );
    }
    foo(); // undefined 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们要记住非常重要的一点:this是在运行的时候进行绑定的,而不是在定义的时候绑定,this的绑定跟函数声明的位置没有关系,主要是取决于函数的调用方式,想要找到this指向谁,我们就要看懂函数是怎么调用的。

    绑定规则

    1.默认绑定

    当一个独立函数正常调用的时候,不带任何修饰的调用

    // 非严格模式下
    var a = 3
    function foo(){
        console.log(this.a) //a
    }
    foo() 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这种情况下 this.a被解析成了了 全局变量a,this指向是全局对象

    // 严格模式下
    var a = 3
    function foo(){
        "use strict" 
        console.log(this.a) //TypeError
    }
    foo() 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    严格模式下 this不会指向全局对象 this绑定的是undefined

    2.隐式绑定

    调用位置上是否有上下文对象

    function foo(){
        console.log(this.a)
    }
    var obj = {
        a:2,
        foo:foo
    }
    obj.foo() //2 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    调用位置会使用obj上下文对象来引用函数,foo被调用的时候 他的落脚点指向是obj对象,隐式绑定的规则就会把this指向这个上下文对象。所以this.a就跟 obj.a是一样的

    function foo(){
        console.log(this.a)
    }
    var obj = {
        a:2,
        foo:foo
    }
    var obj2 = {
        a:3,
        obj:obj
    }
    obj2.obj.foo() //2 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    当多层调用的时候 只有最后一层才会影响函数的调用位置 比如上面这个 this绑定的还是 obj 而不是obj2

    注意

    隐式绑定会出现隐式丢失的问题,会失去绑定对象,最后应用默认绑定

    var a = 3;
    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    }
    var bar = obj.foo
    bar() //3 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    bar 是 obj.foo的一个引用 他引用的是foo函数本身,此时bar就是一个不带任何修饰的函数调用 应用默认绑定

    var a = 3;
    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    }
    setTimeout( obj.foo, 100 ) //3
    setTimeout(function(fn){
        fn()
    },100,obj.foo) //3 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    参数传递也是一种隐式赋值,回调函数丢失this是非常常见的…

    3.显式绑定

    隐式绑定的时候我们必须在一个对象内部包含一个指向函数的属性,然后通过属性间接引用函数,把这个this间接隐式的绑定到这个对象上
    如果我们不想在对象内部包含函数的引用 ,而想在某个对象上强制调用函数
    我们可以把这个函数绑定到对象的原型上,也算是不用再对象内部包含函数了吧…
    更好的办法是我们可以使用函数的 call() apply() bind() 这种方法

    function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2
    }
    foo.call(obj) //2
    foo.apply(obj) //2 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果你第一个参数传入的是一个原始类型 比如字符串 布尔 数字作为绑定对象 这些原始类型会被转换为 对象的形式 new String() new Number()…

    硬绑定

    Function.prototype.bind()
    function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2
    }
    var obj2 = {
        a:3
    }
    var bar = foo.bind(obj)  //会返回一个硬编码的新函数 他会把参数设置为this的上下文
    bar.call(obj2) //2  返回的新函数 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    有些api 方法 会提供一个可选参数 context 其作用跟bind一样 确保你的回调函数使用指定的this 比如 array.forEach(fn,context)…

    4.new绑定

    使用new 来调用函数的时候会执行以下操作
    1.创建一个全新的对象
    2.这个新对象会被执行原型的链接
    3.新对象会绑定到函数调用的this
    4.如果没有返回其他的对象,那么函数会自动返回这个对象

    function Foo(a){
        this.a = a
    }
    var bar = new Foo(2)
    console.log(bar.a) //2 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用new 来调用Foo函数 会构造一个新对象并把它绑定到Foo调用中的this上 然后返回了

    优先级

    函数不带任何修饰的时候单独调用才会触发默认绑定 所以说默认绑定是优先级最低的了

    那剩下三个规则哪个的优先级最高?

    显示绑定跟隐式绑定比较

    function foo(){
        console.log(this.a)
    }
    var obj1 = {
        a:1,
        foo:foo
    }
    
    var obj2 = {
        a:2,
        foo:foo
    }
    obj1.foo() //1
    obj2.foo() //2
    
    obj1.foo.call(obj2) //2
    obj2.foo.call(obj1) //1 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以看到 显示绑定的优先级还是更高一点

    new 绑定跟隐式绑定比较

    function foo(arg){
        this.a = arg
    }
    
    var obj1 ={
        foo:foo
    }
    var obj2 ={}
    
    obj1.foo(2)
    console.log(obj1.a) //2
    
    var bar = new obj1.foo(4)
    console.log(obj1.a) //2
    console.log(bar.a) //4 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以看到 new绑定的优先级比隐式绑定要高

    new 绑定跟显示绑定比较

    new跟call apply无法一起使用 无法通过new foo.call(obj),试一下硬绑定

    在这里插入代码片 
    
    • 1
    function foo(arg){
        this.a = arg
    }
    var obj1 ={}
    var bar = foo.bind(obj1)
    bar(3)
    console.log(obj1.a) //3
    
    var baz = new bar(4)
    console.log(baz.a) //4
    console.log(obj1.a) //3 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    new 调用bar修改了硬绑定时候的 函数的this new的优先级高一点

    所以我们可以根据下面的优先级规则进行判断了

    1.函数是否在new中调用 是的话this绑定新创建的对象 var bar = new Foo()
    2.函数是否通过call apply 显示绑定或者是 bind硬绑定 如果是的话this指向指定的对象 foo.call(obj)
    3.函数是否在某个上下文中调用 隐式绑定,如果是 this绑定那个上下文对象 注意绑定丢失的问题
    4.如果都不是 就是默认绑定非严格模式下绑定的是全局对象 严格模式下绑定的是undefined

    绑定例外

    1.将null和undefined作为call apply参数 作为this绑定对象的时候 这些值会被忽略 应用的是默认绑定

    var a =3
    function foo(){
        console.log(this.a) //3
    }
    foo.call(null) 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.箭头函数

    function foo(){
        return ()=>{
            console.log(this.a)
        }
    }
    var obj1 = {
        a:3
    }
    var obj2 = {
        a:4
    }
    var bar = foo.call(obj1)
    bar.call(obj2) //3  this绑定的是obj1 而不是obj2!!! 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在看一个

    function foo(){
        setTimeout(()=>{
            console.log(this.a) //2
        },100)
    }
    var obj = {
        a:2
    }
    foo.call(obj) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    箭头函数不使用this绑定的四种规则,而是根据外层作用域来决定this的,外层作用域的this绑定的是什么 他的this就是什么





    转自:csdn 论坛 作者:Selfimpr欧

    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服

    7种经常使用的Vue.js模式和36个实用Vue开发技巧,你知道多少?

    前端达人

    7种Vue.js模式

    1.处理加载状态

    在大型应用程序中,我们可能需要将应用程序划分为更小的块,只有在需要时才从服务器加载组件。为了使这一点更容易,Vue允许你将你的组件定义为一个工厂函数,它异步解析你的组件定义。Vue只有在需要渲染组件时才会触发工厂函数,并将缓存结果,以便将来重新渲染。2.3版本的新功能是,异步组件工厂也可以返回一个如下格式的对象。

    const AsyncComponent = () => ({
      // 要加载的组件(应为Promise)
      component: import('./MyComponent.vue'),
      // 异步组件正在加载时要使用的组件
      loading: LoadingComponent,
      // 加载失败时使用的组件
      error: ErrorComponent,
      // 显示加载组件之前的延迟。默认值:200ms。
      delay: 200,
      // 如果提供并超过了超时,则会显示error组件。默认值:无穷。
      timeout: 3000
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通过这种方法,你有额外的加载和错误状态、组件获取的延迟和超时等选项。

    2.廉价的“v-once”静态组件

    在Vue中渲染纯HTML元素的速度非常快,但有时你可能有一个包含大量静态内容的组件。在这种情况下,你可以通过在根元素中添加 v-once 指令来确保它只被评估一次,然后进行缓存,就像这样。

    Vue.component('terms-of-service', {
      template: `
        <div v-once>
          <h1>Terms of Service</h1>
          ... a lot of static content ...
        </div>
      `
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.递归组件

    组件可以在自己的模板中递归调用自己,但是,它们只能通过 name 选项来调用。

    如果你不小心,递归组件也可能导致无限循环:

    name: 'stack-overflow',
    template: '<div><stack-overflow></stack-overflow></div>' 
    
    • 1
    • 2

    像上面这样的组件会导致“超过最大堆栈大小”的错误,所以要确保递归调用是有条件的即(使用 v-if 最终将为 false

    4.内联模板

    当特殊属性 inline-template 存在于一个子组件上时,该组件将使用它的内部内容作为它的模板,而不是将其视为分布式内容,这允许更灵活的模板编写。

    <my-component inline-template>
      <div>
        <p>These are compiled as the component's own template.</p>
        <p>Not parent's transclusion content.</p>
      </div>
    </my-component> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.动态指令参数

    指令参数可以是动态的。例如,在 v-mydirective:[argument]=“value" 中, argument 可以根据组件实例中的数据属性更新!这使得我们的自定义指令可以灵活地在整个应用程序中使用。

    这是一条指令,其中可以根据组件实例更新动态参数:

    <div id="dynamicexample">
      <h3>Scroll down inside this section ↓</h3>
      <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
    </div>
    Vue.directive('pin', {
      bind: function (el, binding, vnode) {
        el.style.position = 'fixed'
        var s = (binding.arg == 'left' ? 'left' : 'top')
        el.style[s] = binding.value + 'px'
      }
    })
    
    new Vue({
      el: '#dynamicexample',
      data: function () {
        return {
          direction: 'left'
        }
      }
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6.事件和键修饰符

    对于 .passive.capture 和 .once 事件修饰符,Vue提供了可与 on 一起使用的前缀:

    例如:

    on: {
      '!click': this.doThisInCapturingMode,
      '~keyup': this.doThisOnce,
      '~!mouseover': this.doThisOnceInCapturingMode
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对于所有其他的事件和键修饰符,不需要专有的前缀,因为你可以在处理程序中使用事件方法。

    7.依赖注入(Provide/Inject)

    有几种方法可以让两个组件在 Vue 中进行通信,它们各有优缺点。在2.2版本中引入的一种新方法是使用Provide/Inject的依赖注入。

    这对选项一起使用,允许一个祖先组件作为其所有子孙的依赖注入器,无论组件层次结构有多深,只要它们在同一个父链上。如果你熟悉React,这与React的上下文功(context)能非常相似。

    // parent component providing 'foo'
    var Provider = {
      provide: {
        foo: 'bar'
      },
      // ...
    }
    
    // child component injecting 'foo'
    var Child = {
      inject: ['foo'],
      created () {
        console.log(this.foo) // => "bar"
      }
      // ...
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    36个Vue开发技巧

    1.require.context()

    1.场景:如页面需要导入多个组件,原始写法:

    importtitleComfrom'@/components/home/titleCom'
    importbannerComfrom'@/components/home/bannerCom'
    importcellComfrom'@/components/home/cellCom'
    components:{titleCom,bannerCom,cellCom} 
    
    • 1
    • 2
    • 3
    • 4

    2.这样就写了大量重复的代码,利用require.context可以写成

    constpath=require('path')
    constfiles=require.context('@/components/home',false,/\.vue$/)
    constmodules={}
    files.keys().forEach(key=>{
    constname=path.basename(key,'.vue')
    modules[name]=files(key).default||files(key)
    })
    components:modules 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样不管页面引入多少组件,都可以使用这个方法

    3.API方法

    实际上是webpack的方法,vue工程一般基于webpack,所以可以使用require.context(directory,useSubdirectories,regExp)
    接收三个参数:directory:说明需要检索的目录useSubdirectories:是否检索子目录regExp:匹配文件的正则表达式,一般是文件名

    2.watch

    2.1常用用法

    1.场景:表格初始进来需要调查询接口getList(),然后input改变会重新查询

    created(){
        this.getList()
    },
    watch:{
         inpVal(){
            this.getList()
          }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2立即执行

    2.可以直接利用watch的immediate和handler属性简写

    watch:{
         inpVal:{
            handler:'getList',
                 immediate:true
            }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.3深度监听

    3.watch的deep属性,深度监听,也就是监听复杂数据类型

    watch:{
         inpValObj:{
            handler(newVal,oldVal{
                console.log(newVal)
                console.log(oldVal)
         },
         deep:true
       }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    此时发现oldVal和newVal值一样;因为它们索引同一个对象/数组,Vue不会保留修改之前值的副本;所以深度监听虽然可以监听到对象的变化,但是无法监听到具体对象里面那个属性的变化

    7种vue模式还能和大家说完,但36个vue开发技巧太多啦,文章篇幅也不够,小编写了两个例子,没写出来的开发技巧小伙伴们请点击这里领取Vue开发必须知道的36个技巧PDF文档。

    转自:csdn 论坛 作者:李不要熬夜

    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


    JavaScript控制语句与异常处理

    前端达人


    一、if 控制语句

    1. 简单if语句
     if(条件表达式){ 表达式成立时执行的代码段 } 
    
    • 1
    • 2
    • 3

    注意 : 除零值以外,其他值都为真

    特殊写法 : { }可以省略,一旦省略,if语句只控制其后的第一行代码

    var a = 10; if(a<5){ console.log('成立'); //如果if不成立则不运行 } if(a<5)console.log('成立'); //如果if不成立则不运行 console.log('测试'); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. if - else结构
    if(条件表达式){ //条件成立时执行 }else{ //条件不成立时选择执行 
    
    • 1
    • 2
    • 3
    • 4
    1. 多重分支结构
    if(条件1){ //条件1成立时执行 }else if(条件2){ //条件2成立时执行 }else if(条件3){ //条件3成立时执行 }...else{ //条件不成立时执行 } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例:

    var bmi=23.5; if(bmi>23.5){ console.log('肥胖') }else if(bmi>=18.5){ console.log('正常') }else{ console.log('偏瘦') } //正常 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二、switch 选择控制语句

    语法:

    switch(value){ case1 : //value与值1匹配全等时,执行的代码段 break; //结束匹配 case2 : //value与值2匹配全等时,执行的代码段 break; case3 : //value与值3匹配全等时,执行的代码段 break; default: //所有case匹配失败后默认执行的语句 break; } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    使用 :

    1. switch语句用于值的匹配,case用于列出所有可能的值;只有switch()表达式的值与case的值匹配全等时,才会执行case对应的代码段

    2. break用于结束匹配,不再向后执行;可以省略,break一旦省略,会从当前匹配到的case开始,向后执行所有的代码语句,直至结束或碰到break跳出

    3. default用来表示所有case都匹配失败的情况,一般写在末尾,做默认操作

    4. 多个case共用代码段

      case 值1:

      case 值2:

      case 值3:

      以上任意一个值匹配全等都会执行的代码段

    注:case表示一个条件,满足这个条件就会进入该内部,遇到break则会跳出,若未写break则直到遇到下一个break才会停止。

    var gameScore = 'better'; switch(gameScore){ //case表示一个条件 满足这个条件就会走进来 遇到break跳出。如果某个条件中不写 break,那么直到该程序遇到下一个break停止 case 'good': console.log('玩的很好') //break表示退出 break; case 'better': console.log('玩的老牛逼了') break; case 'best': console.log('恭喜你 吃鸡成功') break; default: console.log('很遗憾') } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    三、while 循环控制语句

    语法:

    定义循环变量; while(循环条件){ 条件满足时执行的代码段
       更新循环变量; } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    //循环变量 var i = 1; var sum=0; while(i<101){ //循环体 console.log(i); sum+=i; //更新循环变量 i++; } console.log(sum); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、do-while 循环

    语法:

    do{ 循环体; 更新循环变量 }while(循环条件); 
    
    • 1
    • 2
    • 3
    • 4

    示例:

    //循环输出一百次 var i = 101; do{ console.log('python'); //更新循环变量 i++; }while(i<101); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    do-while与while循环的区别:

    • while循环先判断循环条件,条件成立才执行循环体
    • do-while循环不管条件是否成立,先执行一次循环体

    五、for 循环控制语句

    语法:

    for(定义循环变量;循环条件;更新循环变量){ 循环体; } 
    
    • 1
    • 2
    • 3

    循环控制 :

    1. break 强制结束循环
    2. continue 结束当次循环,开始下一次循环

    循环嵌套 : 在循环中嵌套添加其他循环

    示例:

    /*控制循环体的执行:break/continue*/ for(var i=1;i<10;i++){ console.log(i); if(i==5){ console.log('--------') //一旦执行break,循环体中后面的代码都不执行 break; } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注:循环的是获取的每一个DOM元素值,for...in...是用来循环对象的所有属性,DOM元素包括了输出的属性,因此不推荐使用for...in...


    六、三元运算符

    var a = 1 var b =2 var c = a>b ? a:b //如果a>b成立返回a,否则返回b console.log(c) 
    
    • 1
    • 2
    • 3
    • 4

    七、异常处理

    语法:

    try{ 这段代码从上至下运行,其中任何一个语句抛出异常该代码块即结束运行。 }catch(e){ 如果try代码块中抛出异常,catch代码块中代码就会被执行;
        e为一个局部变量,用来指向Error对象或者其他抛出的对象。 }finally{ 无论try中代码是否有异常抛出(甚至是try代码块中有return语句)finally代码块中始终会被执行。 } 
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注:主动抛出异常:throw Error("xxx")


    转自:csdn 论坛 作者:PPPsych


    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服



    我劝你学一下TypeScript(TypeScipt有这篇就够了)

    前端达人

    TypeScript

    一 概述

    1.1 基本介绍

    • 以JavaSrcipt为基础构建的语言
    • 一个JavaSrcipt的超集
    • 可以再任何支持JavaSrcipt的平台运行
    • TypeScript扩展了JavaSrcipt,并添加了变量类型
    • TS不能被JS解析器中直接执行,需要通过编译转换为JS

    1.2 安装TS编译器

    • 安装
    npm i -g typescript 
    
    • 1
    • 验证
    tsc 
    
    • 1

    在这里插入图片描述

    • 测试

      • 先建hello.ts
      console.log("你好啊,TS") 
              
      • 1
      • 编译
      tsc hello.ts 
              
      • 1

    在这里插入图片描述

    二 基本语法

    2.1 总体介绍

    JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。

    原始数据类型包括:布尔值、数值、字符串、nullundefined 以及 ES6 中的新类型 Symbol 和 BigInt。4

    • 类型声明

      • 类型声明是TS非常重要的一个特点;

      • 通过类型声明可以指定TS中变量(参数、形参)的类型;

      • 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错;

      • 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值;

      • 语法:

        • let 变量: 类型;
          
          let 变量: 类型 = 值;
          
          function fn(参数: 类型, 参数: 类型): 类型{
              ...
          } 
                                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
    • 变量:

    //申明一个变量 let 变量名:类型
    // number
    let a:number;
    a=1;
    // string
    let b:string;
    b="哈哈";
    // boolean
    let c:boolean=false; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 函数:
    // 函数 function 函数名(变量:类型,变量:类型):返回值类型 {
    //     return a+b;
    // }
    function sum(a:number,b:number):number {
        return a+b;
    }
    sum(1,3) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 自动类型判断
      • TS拥有自动的类型判断机制
      • 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
      • 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明

    2.2 基本数据类型

    类型 例子 描述
    number 1, -33, 2.5 任意数字
    string ‘hi’, “hi”, hi 任意字符串
    boolean true、false 布尔值true或false
    字面量 其本身 限制变量的值就是该字面量的值
    any * 任意类型
    unknown * 类型安全的any
    void 空值(undefined) 没有值(或undefined)
    never 没有值 不能是任何值
    object {name:‘孙悟空’} 任意的JS对象
    array [1,2,3] 任意JS数组
    tuple [4,5] 元素,TS新增类型,固定长度数组
    enum enum{A, B} 枚举,TS中新增类型
    • number
    // number let a:number; a=1; 
    
    • 1
    • 2
    • 3
    • string
    // string
    let b:string;
    b="哈哈"; 
    
    • 1
    • 2
    • 3
    • boolean
    // boolean
    let c:boolean=false; 
    
    • 1
    • 2
    • 字面量
    // 字面量 |:或 &:与
    let d:'man'|'woman';
    d="man";
    d="woman";
    let e:number|boolean;
    e=1
    e=false; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • any
    //any 一切类型变量与js没啥区别
    let f:any;
    f=1
    f=false; 
    
    • 1
    • 2
    • 3
    • 4
    • unknown
    // unknown 不知道什么类型,实际上是一种安全的any
    let a:unknown;
    a=1;
    a=false;
    a="哈哈哈"; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • void
    // 函数返回值,默认返回值any,空值
    function add(a,b) {
    return a+b;
    }
    // 没有返回值,空值
    function adds():void {
    return null;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • never
    // never:永远没有返回值
    function hh():never {
    throw new Error("错误了");
    } 
    
    • 1
    • 2
    • 3
    • 4
    • object
    // object 对象 常用:{属性:属性值}
    let  b:{name:string,age:string,sex:boolean};
    b={name:"小米",age:"12",sex:false}
    // ?可选属性
    let c:{name:string,age?:string,sex?:boolean}
    c={name:"雷军"}
    // 任意类型的属性 [propName:string]:any
    let d:{name:string,[propName:string]:any}
    d={name:"哈哈",a:1,b:2}
    // 限制函数结构 语法:(形参:类型......)=>返回值
    let e:(a:number,b:number)=>number;
    e=function (n1,n2):number {
    return n1+n2;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • array
    // 数组,同一类型的数组 类型[], array<类型>
    let a:string[];
    a=['h','b','c','d']
    let b: Array<string>;
    b=['h','b','c','d'] 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • tuple
    // 元组:固定长度的数组
    let c:[string,string,number]
    c=["哈哈","哈哈",1] 
    
    • 1
    • 2
    • 3
    • enum
    // 枚举
    //性别枚举类
    enum sex {
        male,
        female
    }
    let d:{name:string,sex:sex}
    d={
        name:"李磊",
        sex:sex.male
    }
    alert(d.sex===sex.male) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 别名
    //别名
    type mytype=string;
    let  e:mytype; 
    
    • 1
    • 2
    • 3

    三 编译选项

    3.1 自动编译文件

    • 如果直接使用tsc指令,则可以自动将当前项目下的所有ts文件编译为js文件。
    • 但是能直接使用tsc命令的前提时,要先在项目根目录下创建一个ts的配置文件 tsconfig.json
    • tsconfig.json是一个JSON文件,添加配置文件后,只需只需 tsc 命令即可完成对整个项目的编译

    3.2 具体配置

    { //指定需要TS编译的文件路径 /**:任意路径 /*:任意文件 "include": ["./编译选项/src/**/*"], //指定不需要TS编译的文件路径  /**:任意路径 /*:任意文件 ,默认路径: "exclude": ["./编译选项/test/**/*","./基本数据类型/**/*"], //继承:配置文件的重复引用 // "extends": "", //具体需要编译的文件 //"files": [], //编译器选项 "compilerOptions": { // target 指定被TS编译的ES版本 "target": "ES3", // module 模块化 "module": "system", // 指定项目需要的库 "lib": ["dom"], // 编译完后的js文件路径 "outDir": "./编译选项/js", // 将代码合并为一个文件 "outFile": "./代码/1.js", // 是否对js文件进行编译 "allowJs": true, // 检查js是否符合规范 "checkJs": false, // 是否移除注释 "removeComments": true, // 不生成编译后的文件 "noEmit": false, // 当有错误时不生编译文件 "noEmitOnError": true, // 是否是严格模式 "alwaysStrict": true, // 是否允许出现隐式的any "noImplicitAny": false, // 是否允许出现隐式的this "noImplicitThis": false, // 严格使用空值 "strictNullChecks": false } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 其他配置:https://www.w3cschool.cn/typescript/typescript-compiler-options.html

    四 打包选项

    通常情况下,实际开发中我们都需要使用构建工具对代码进行打包;TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS;

    4.1 初始化项目

    进入项目根目录,执行命令 npm init -y,创建package.json文件

    4.2 下载构建工具

    命令如下:

    npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin 
    
    • 1

    共安装了7个包:

    • webpack:构建工具webpack
    • webpack-cli:webpack的命令行工具
    • webpack-dev-server:webpack的开发服务器
    • typescript:ts编译器
    • ts-loader:ts加载器,用于在webpack中编译ts文件
    • html-webpack-plugin:webpack中html插件,用来自动创建html文件
    • clean-webpack-plugin:webpack中的清除插件,每次构建都会先清除目录

    4.3 配置TS编译选项

    根目录下创建tsconfig.json,配置可以根据自己需要

    { "compilerOptions": { "target": "ES2015", "module": "ES2015", "strict": true } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.4 修改package.json配置

    修改package.json添加如下配置

    { ... "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "start": "webpack serve --open chrome.exe" }, ... } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.5 项目使用

    在src下创建ts文件,并在并命令行执行npm run build对代码进行编译;

    或者执行npm start来启动开发服务器;

    4.6 Babel

    除了webpack,开发中还经常需要结合babel来对代码进行转换;

    以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中;

    虽然TS在编译时也支持代码转换,但是只支持简单的代码转换;

    对于例如:Promise等ES6特性,TS无法直接转换,这时还要用到babel来做转换;

    安装依赖包:

    npm i -D @babel/core @babel/preset-env babel-loader core-js 
    
    • 1

    共安装了4个包,分别是:

    • @babel/core:babel的核心工具
    • @babel/preset-env:babel的预定义环境
    • @babel-loader:babel在webpack中的加载器
    • core-js:core-js用来使老版本的浏览器支持新版ES语法

    修改webpack.config.js配置文件

    ...
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: "babel-loader",
                        options:{
                            presets: [
                                [
                                    "@babel/preset-env",
                                    {
                                        "targets":{
                                            "chrome": "58",
                                            "ie": "11"
                                        },
                                        "corejs":"3",
                                        "useBuiltIns": "usage"
                                    }
                                ]
                            ]
                        }
                    },
                    {
                        loader: "ts-loader",
    
                    }
                ],
                exclude: /node_modules/
            }
        ]
    }
    ... 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    如此一来,使用ts编译后的文件将会再次被babel处理;使得代码可以在大部分浏览器中直接使用;同时可以在配置选项的targets中指定要兼容的浏览器版本;

    • 总结
    // 导入包 const path = require("path"); // 导入HTML插件 const HtmlWebpackPlugin = require("html-webpack-plugin"); // 导入清除插件 const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //webpack打包配置文件 module.exports={ // 关闭代码压缩,可选 optimization:{ minimize: false }, //入口 entry: './src/index.ts', //指定打包文件所在目录 output: { //打包文件所在目录 path: path.resolve(__dirname,'dist'), //打包文件名 filename: "bundle.js", // 关闭webpack的箭头函数,可选 environment: { arrowFunction: false // 关闭webpack的箭头函数,可选 } }, //指定打包需要的模块 module: { //规则 rules: [ { // 指定规则生效的文件 test: /\.ts$/, // 要使用的loader加载器 use: [ // 加载器一 { loader: "babel-loader", // 选项 options:{ //预设 presets: [ [ //指定环境插件 "@babel/preset-env", { // 兼容的浏览器信息 "targets":{ "chrome": "58", "ie": "11" }, //  指定corejs版本 "corejs":"3", // 使用core的方法 usage:按需加载 "useBuiltIns": "usage" } ] ] } }, // 加载器二 { loader: "ts-loader", } ] , // 排除的模块 exclude: /node_modules/ } ] }, //配置Html插件 plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ //标题 //title: "小米" // 模板 template: "./src/index.html" }) ], // 用来设置引用模块 resolve: { // 以 js ts 结尾 extensions: ['.js','.ts'] } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    五 OOP

    要想面向对象,操作对象,首先便要拥有对象;

    要创建对象,必须要先定义类,所谓的类可以理解为对象的模型;

    程序中可以根据类创建指定类型的对象;

    举例来说:

    可以通过Person类来创建人的对象,通过Dog类创建狗的对象,不同的类可以用来创建不同的对象;

    5.1 定义类

    class 类名 {
        属性名: 类型;
    
        constructor(参数: 类型){
            this.属性名 = 参数;
        }
    
        方法名(){
            ....
        }
    
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    示例:

     class Person{
            name: string;
            age: number;
    
            constructor(name: string, age: number){
                this.name = name;
                this.age = age;
            }
    
            sayHello(){
                console.log(`大家好,我是${this.name}`);
            }
        } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用类:

    const p = new Person('孙悟空', 18);
    p.sayHello(); 
    
    • 1
    • 2

    5.2 构造函数

    可以使用constructor定义一个构造器方法;

    注1:在TS中只能有一个构造器方法!

    例如:

    class C{
        name: string;
        age: number
    
        constructor(name: string, age: number) {
            this.name = name;
            this.age = age;
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    同时也可以直接将属性定义在构造函数中:

    class C {
        constructor(public name: string, public age: number) {
        }
    } 
    
    • 1
    • 2
    • 3
    • 4

    上面两种定义方法是完全相同的!

    注2:子类继承父类时,必须调用父类的构造方法(如果子类中也定义了构造方法)!

    例如:

    class A {
        protected num: number;
        constructor(num: number) {
            this.num = num;
        }
    }
    
    class X extends A {
        protected name: string;
        constructor(num: number, name: string) {
            super(num);
            this.name = name;
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果在X类中不调用super将会报错!

    5.3 封装

    对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装

    默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在TS中可以对属性的权限进行设置

    • 静态属性(static):
      • 声明为static的属性或方法不再属于实例,而是属于类的属性;
    • 只读属性(readonly):
      • 如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
    • TS中属性具有三种修饰符:
      • public(默认值),可以在类、子类和对象中修改
      • protected ,可以在类、子类中修改
      • private ,可以在类中修改

    示例:

    public:

    class Person{
        public name: string; // 写或什么都不写都是public
        public age: number;
    
        constructor(name: string, age: number){
            this.name = name; // 可以在类中修改
            this.age = age;
        }
    
        sayHello(){
            console.log(`大家好,我是${this.name}`);
        }
    }
    
    class Employee extends Person{
        constructor(name: string, age: number){
            super(name, age);
            this.name = name; //子类中可以修改
        }
    }
    
    const p = new Person('孙悟空', 18);
    p.name = '猪八戒';// 可以通过对象修改 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    protected:

    class Person{ protected name: string; protected age: number; constructor(name: string, age: number){ this.name = name; // 可以修改 this.age = age; } sayHello(){ console.log(`大家好,我是${this.name}`); } } class Employee extends Person{ constructor(name: string, age: number){ super(name, age); this.name = name; //子类中可以修改 } } const p = new Person('孙悟空', 18); p.name = '猪八戒';// 不能修改 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    private:

    class Person{ private name: string; private age: number; constructor(name: string, age: number){ this.name = name; // 可以修改 this.age = age; } sayHello(){ console.log(`大家好,我是${this.name}`); } } class Employee extends Person{ constructor(name: string, age: number){ super(name, age); this.name = name; //子类中不能修改 } } const p = new Person('孙悟空', 18); p.name = '猪八戒';// 不能修改 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.4 属性存取器

    对于一些不希望被任意修改的属性,可以将其设置为private

    直接将其设置为private将导致无法再通过对象修改其中的属性

    我们可以在类中定义一组读取、设置属性的方法,这种对属性读取或设置的属性被称为属性的存取器

    读取属性的方法叫做setter方法,设置属性的方法叫做getter方法

    示例:

    class Person{ private _name: string; constructor(name: string){ this._name = name; } get name(){ return this._name; } set name(name: string){ this._name = name; } } const p1 = new Person('孙悟空'); // 实际通过调用getter方法读取name属性 console.log(p1.name); // 实际通过调用setter方法修改name属性  p1.name = '猪八戒'; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.5 静态属性

    静态属性(方法),也称为类属性。使用静态属性无需创建实例,通过类即可直接使用

    静态属性(方法)使用static开头

    示例:

    class Tools{ static PI = 3.1415926; static sum(num1: number, num2: number){ return num1 + num2 } } console.log(Tools.PI); console.log(Tools.sum(123, 456)); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.6 this

    在类中,使用this表示当前对象

    5.7 继承

    继承时面向对象中的又一个特性

    通过继承可以将其他类中的属性和方法引入到当前类中

    示例:

    class Animal{ name: string; age: number; constructor(name: string, age: number){ this.name = name; this.age = age; } } class Dog extends Animal{ bark(){ console.log(`${this.name}在汪汪叫!`); } } const dog = new Dog('旺财', 4); dog.bark(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    通过继承可以在不修改类的情况下完成对类的扩展

    5.8 重写

    发生继承时,如果子类中的方法会替换掉父类中的同名方法,这就称为方法的重写

    示例:

    class Animal{ name: string; age: number; constructor(name: string, age: number){ this.name = name; this.age = age; } run(){ console.log(`父类中的run方法!`); } } class Dog extends Animal{ bark(){ console.log(`${this.name}在汪汪叫!`); } run(){ console.log(`子类中的run方法,会重写父类中的run方法!`); } } const dog = new Dog('旺财', 4); dog.bark(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在子类中可以使用super来完成对父类的引用

    5.9抽象类(abstract class)

    抽象类是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例

    abstract class Animal{ abstract run(): void; bark(){ console.log('动物在叫~'); } } class Dog extends Animals{ run(){ console.log('狗在跑~'); } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用abstract开头的方法叫做抽象方法,抽象方法没有方法体只能定义在抽象类中,继承抽象类时抽象方法必须要实现;

    5.10 接口

    接口的作用类似于抽象类,不同点在于:接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法;

    接口主要负责定义一个类的结构,接口可以去限制一个对象的接口:对象只有包含接口中定义的所有属性和方法时才能匹配接口;

    同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性;

    示例(检查对象类型):

    interface Person{ name: string; sayHello():void; } function fn(per: Person){ per.sayHello(); } fn({name:'孙悟空', sayHello() {console.log(`Hello, 我是 ${this.name}`)}}); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    示例(实现):

    interface Person{ name: string; sayHello():void; } class Student implements Person{ constructor(public name: string) { } sayHello() { console.log('大家好,我是'+this.name); } } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.11泛型

    定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定);

    此时泛型便能够发挥作用;

    举个例子:

    function test(arg: any): any{
        return arg;
    } 
    
    • 1
    • 2
    • 3

    上例中,test函数有一个参数类型不确定,但是能确定的时其返回值的类型和参数的类型是相同的;

    由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的:

    首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型;

    5.12 泛型函数

    5.12.1 创建泛型函数

    function test<T>(arg: T): T{
        return arg;
    } 
    
    • 1
    • 2
    • 3

    这里的``就是泛型;

    T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型;

    所以泛型其实很好理解,就表示某个类型;

    那么如何使用上边的函数呢?

    5.12.2 使用泛型函数

    5.12.3 方式一(直接使用):
    test(10) 
    
    • 1

    使用时可以直接传递参数使用,类型会由TS自动推断出来,但有时编译器无法自动推断时还需要使用下面的方式

    5.12.4 方式二(指定类型):
    test<number>(10) 
    
    • 1

    也可以在函数后手动指定泛型;

    5.12.5 函数中声明多个泛型

    可以同时指定多个泛型,泛型间使用逗号隔开:

    function test<T, K>(a: T, b: K): K{
      return b;
    }
    
    test<number, string>(10, "hello"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用泛型时,完全可以将泛型当成是一个普通的类去使用;

    2.12.6 泛型类

    类中同样可以使用泛型:

    class MyClass<T>{
      prop: T;
    
      constructor(prop: T){
          this.prop = prop;
      }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.12.7 泛型继承

    除此之外,也可以对泛型的范围进行约束

    interface MyInter{
      length: number;
    }
    
    function test<T extends MyInter>(arg: T): number{
      return arg.length;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用T extends MyInter表示泛型T必须是MyInter的子类,不一定非要使用接口类和抽象类同样适用;


    转自:csdn 论坛 作者:Eason~IT


    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


    闭包--没有那么复杂!

    前端达人

    一弄JS,就总有人在提闭包、闭包的,此前不了解,上网上搜,很多讲的也过于难懂,感觉没有必要那么复杂,今天就把我对于闭包的理解总结一下,力求简单、通俗、易懂



    目录



    1.什么是闭包



    2.闭包有什么用?



    3.闭包带来了什么问题呢?



    4.总结



     1.什么是闭包

    通俗的理解就是:子函数使用着父函数作用域内的变量,导致父函数作用域内的变量无法回收释放的这种情况。



    <script>

    function a(){

       let num=0;

       return ()=>{

         console.log(num++);

       }

    };

    let t=a();

    //执行三次结果是多少?

    t();

    t();

    t();

    </script>

     看上面的代码,分别执行三次t(),那么结果是多少呢?







    结果是0,1,2,这是怎么回事呢?原因就是闭包导致的。



    我们首先分析:a函数内有一个num变量和一个子函数,在子函数中使用着父函数a的变量num,使其自加1。随后将a函数执行后复制给t,由于子函数一直在使用num变量(其实不论子函数是否使用,父函数的变量都不会释放,随时等待子函数调用除非子函数引用被释放),所以num变量并不会回收释放。也就是说三次t()改变的num其实是一个。这就是闭包的原因,如果没有闭包,在子函数处理完num后,num回收,则应该是三个0。



    2.闭包有什么用?

    从另一个角度理解,如果一个函数没有被回收释放,那么他的父级、父级的父级。。。。。祖宗级(全局)的变量也不会被释放,而且可以被函数访问到。这就是为什么全局变量在哪里都可以访问的原因了,其实就是闭包的应用。那什么时候全局变量不可用了呢,就是他内部的子函数引用都被清空的时候,也就是程序关闭了,网页关闭了之后。



    1.绝大多数时候我们不会有意识的用到闭包,但是没有闭包你得程序是跑不了的,比如全局变量。。。。。



    2.极少数情况下我们用函数去给一个变量“续命”。这种情况下,闭包就会被用到了。



    3.还有就是,如果问你:



    JS中没有严格意义的私有变量,请问你能弄出来绝对私有么?答案:能。请看代码:



    <script>

      //这个num就是真的严格意义的私有

      let a=(function (){

        let num =0;

        return {

          get(){

            return num;

          },

          set(val){

            num=val;

          }

        }

      })();

      a.set(55);

      console.log(a.get());

    </script>

    这时a将返回一个json,里面有两个函数,分别是get和set。当你在外部给num赋值或者是调用num时,必须通过get和set“属性访问器”去访问,否则无法访问到num变量(真正的私有)。因为当你不用get和set时候也就是说get和set函数是不存在的,那么根据“闭包的原理”num会被释放掉,也就是说num不存在了,所以你直接num赋值是undefined的。



    3.闭包带来了什么问题呢?

    这里最为典型的就是for循环中用var定义循环变量(var i=0;i<10;i++),循环的结果是相同的,就是最后一次运算的结果。这里就涉及到了作用域和闭包的问题。详细的请看我的这篇文章,本文就不赘述了。



    深入理解js_for循环条件中使用var为什么会出问题?(js块级作用域理解)


    转自:csdn 论坛 作者:韦_恩


    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


    JavaScript数据类型 --分享

    前端达人


    前言

    这部分来学习关于JavaScript数据类型的相关知识。


    一、字面量

    用于表达一个固定值的表示法,又叫做常量。

    1.数字字面量

    在这里插入图片描述

    <script> // 整数字面量 // 十进制 console.log(12); // 八进制 console.log(010); // 十六进制 console.log(0x100); </script  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    效果展示
    在这里插入图片描述

    2.浮点数字面量

    浮点数不区分进制,所有的浮点数都是十进制下的(注意:浮点数若是0~1之间的,前面的0可以省略不写,例如0.6可以写成.6)浮点数的精度远远不如小数。

    // 浮点数字面量 console.log(1.2); console.log(-1.2); console.log(0.1343); console.log(.1343); console.log(1.2e4); console.log(1.2e-4);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    效果展示
    在这里插入图片描述

    3.特殊值

    Infinity:无穷

    // Infinity console.log(Number.MAX_VALUE); console.log(Number.MIN_VALUE); console.log(5e789); console.log(-5e789); console.log(Infinity);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果展示
    在这里插入图片描述
    NaN:不是一个正常的数
    isNaN()判断一个数据是不是NaN.

    // NaN console.log(0 / 0); console.log(isNaN(0 / 0)); console.log(isNaN(22));  
    
    • 1
    • 2
    • 3
    • 4

    效果展示
    在这里插入图片描述

    4.字符串字面量

    字符串字面量
    用单引号或者双引号包裹起来,引号中间可以有任意多个字符,也可以没有字符则为空字符串。(注意:如果字符串中包含了双引号,则外面用单引号包裹,反之相同)

    // 字符串字面量 console.log("这是一个'字符串'"); console.log("123"); console.log("000%$*^"); console.log("     "); console.log("");  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果展示
    在这里插入图片描述
    **转义符**:
    在这里插入图片描述

    // 转义符 console.log("你\n好"); console.log("你\t好"); console.log("你好\"呀\"");  
    
    • 1
    • 2
    • 3
    • 4

    效果展示
    在这里插入图片描述

    二、变量

    1.变量的定义

    变量:计算机内存储数据的标识符,根据变量名称可以获取到内存中的数据。
    变量声明:使用变量之前先进行定义,创建变量。如果使用之前没有定义变量,就会出现引用错误。
    定义方法:var后面添加空格,空格后面添加一个变量名。
    在这里插入图片描述

    // 变量的定义 var name; var userName; var a; var b1;  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.变量的赋值

    变量定义后有个默认的值为undefined;变量赋值时,内部可以存储任意类型的数据,甚至是一个变量。
    一个var可以同时定义多个变量,中间用逗号分隔开。
    代码如下(示例):

    // 变量的定义 var name; var userName; var a; var b1; // 变量的赋值 var a=1,b=2,c=3; a = a + b + c; console.log(a);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    效果展示:
    在这里插入图片描述

    三、数据类型

    1.简单数据类型

    在这里插入图片描述
    复杂数据类型object。

    2.检测数据类型

    用typeof来检测数据类型,后面加小括号,括号里面写参数。(变量的数据类型是变化的,随着赋值不同数据类型也就不同)
    代码如下(示例):

    console.log(typeof(6.8)); console.log(typeof(-1)); console.log(typeof(NaN)); console.log(typeof(false)); console.log(typeof 66); console.log(typeof 66 + 3);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果展示
    在这里插入图片描述

    3.数据类型转换

    (1)转成字符串
    toString()和String()转换成字符串类型的数据。
    “+”的特殊性,两边只要有一个字符串就是拼接字符串功能。两边是数字那么就是运算功能。
    (2)转成数字类型
    Number()可以将其他类型的数据转换为数字类型。
    parseInt()字符串转成整数的方法:对浮点数可以进行取整操作,将字符串转换为整数数字。(针对数字字符串)
    parseFloat()转成浮点数。
    (3)转成布尔类型
    Boolean()可以转任何类型的数据,将其他的数据转为布尔类型的值。
    转为false : NaN、0、“” 空字符串、null、undefined
    转为true :非0非NaN数字、非空字符串

    // 转成字符串类型 console.log(true.toString()); console.log(String(2333)); console.log(1 + ""); // 转换成数字类型 console.log(Number("11aa")); console.log(parseInt("123.11ac")); //转换成整数 console.log(parseFloat("12.123hh")); // 转成布尔类型 console.log(Boolean(NaN)); console.log(Boolean("")); console.log(Boolean(undefined)); console.log(Boolean("   ")); console.log(Boolean(123));  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.prompt接收数据转换

    // var num = prompt("请输入一个十以内的数字"); // num = parseInt(num); // console.log(typeof(num)); // 简单化写法 var num = parseInt(prompt("请输入一个十以内的数字")); num = num + 3; console.log(num);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结

    数据类型这部分就学习到这里了,坚持学习前端,希望看到一个不一样的自己。

    在这里插入图片描述

    转自:csdn 论坛 作者:G.di


    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


    Ajax是什么?Ajax高级用法之Axios技术

    前端达人

    AJAX

    在这里插入图片描述
    ajax 即 “Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的技术。早期只有同步的方式,多个请求,只能顺序执行,只能等待执行。有了ajax异步技术,可以无需等待上一个请求执行完成,就可以直接发起请求。服务端返回后,ajax通过回调技术通知客户端程序,把响应的结果传递给用户事先写好的回调函数。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页进行局部更新,提升网页的效率,用户无需等待页面的刷新,嗖的一下内容就变化了。改变原有整个页面刷新,造成页面晃眼的现象。所以这项技术一出现,就得到业界的推崇。

    关键字:异步、回调、局部刷新。

    异步请求局部刷新

    主流方式:
    在这里插入图片描述
    技术栈

    • HTML 页面展示
    • JavaScript 形成交互
    • jQuery 封装JavaScript,代码更加简洁高效
    • json 数据传输
    • ajax 异步请求
    • Web中间件(tomcat) 处理请求和响应
    • mysql数据库 存取数据

    用户页面中触发ajax请求,访问后端服务器,Web 中间件 拦截用户请求,转发后端程序进行数据处理,一般还需访问数据库,然后逐层返回。数据库返回数据处理服务,数据处理服务返回Web中间件,Web中间件返回ajax调用,将数据封装到返回的js对象中,目前主流返回数据为json字符串(可以通过Jsonp格式实现跨域访问)。在回调callback的方法中解析json中的数据,最终回显到页面上。通常我们使用jquery封装过的ajax,写法更加简洁灵活。

    Ajax 参数详解

    $.ajax({               //交互方式 $.get,$.post,$.getJSON
        async:              //请求同步异步,默认true异步
        type:               //请求类型:GET/POST             
        url:                //请求的网站地址
        data:               //提交的数据,参数
        contentType:        //请求的MIME媒体类型:application/x-www-form-urlencoded(默认)、application/json;charset=UTF-8
        dataType:           //服务器返回MIME类型:xml/html/script/json/jsonp
        success: function(data){    //请求成功,回调函数,data封装网站返回的数据
            console.log( data );
        },
        error: function(e){     //请求失败回调函数,e封装错误信息
            console.log(e.status);          //状态码
            console.log(e.responseText);    //错误信息
        }
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    js、json、jsonp区别

    json和jsonp的区别

    json请求返回就是json格式,而jsonp请求返回是fun(json)格式。

    • 京东的价格是单独发起ajax请求,返回json数组,一次可以查询多个价格

    查询网址:http://p.3.cn/prices/mgets?skuIds=J_1411013,J_1411014

    [{"p":"-1.00","op":"3888.00","cbf":"0","id":"J_1411013","m":"6699.00"}, {"p":"-1.00","op":"799.00","cbf":"0","id":"J_1411014","m":"1398.00"}] 
    
    • 1
    • 2
    • 京东的商品描述是单独发起ajax请求,返回jsonp格式数据,回显到页面

    查询网址:https://d.3.cn/desc/1411000

    通过JSONView插件展示数据:
    在这里插入图片描述
    网站有一个跨域问题,非同一个网站的请求默认是禁止的**(同源策略)**,那我们的页面要访问其他网站啊,例如:我们访问京东获取其商品的价格信息。这时就必须用jsonp方式进行跨域请求。

    同源策略

    同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。

    json和js对象的区别

    json字符串:   { "id":"100","name":"tony","salary":"30000" }
    js对象:       { "id":100,"name":"tony","salary":30000 } 
    
    • 1
    • 2

    可以看出js对象中value值如果是整数,小数,无需加双引号

    • 含义不同,json是一种数据格式,js对象表示类的实例
    • 传输:json用于跨平台、跨网络传输,速度快;js不能传输
    • 展现:json是字符串不能是对象方法函数;js值不一定加双引号,值可以是对象、函数、字符串等
    • 转换:JSON已经作为浏览器内置对象,eval(json)、JSON.parse(jsonStr) 、JSON.stringify(obj)

    Ajax高级用法(axios)

    Vue中封装了ajax并增强了它,在异步并发处理优于原生ajax。称为:axios(ajax input output system)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../js/vue.js"></script> //引入js支持,axios.min.js文件 <script src="../js/axios.min.js"></script> </head> <body> <div id="app"> 商品价格:{{info}} </div> </body> <script> new Vue({ el: "#app", data(){ //data的ES6函数写法 return { info : null } }, mounted(){ axios.get('../data/price.json') .then( //箭头函数,res为返回值,res.data 返回的json数据对象 res => ( this.info = res.data.price ) ) .catch(function (e){ console.log(e) }) } }) </script> </html> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    注意:
    axios请求头的 Content-Type 默认是 application/json,而postman默认的是 application/x-www-form-urlencoded。

    ajax一般采用@RequestParam接收参数:

    @ResponseBody public Result testpost(@RequestParam String username) {} 
    
    • 1
    • 2

    axiso采用@RequestBody的json方式接收参数

    @ResponseBody public Result testget(@RequestBody Map map) {} 
    
    • 1
    • 2

    为什么要用Axios

    传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。

    JQuery ajax 是对原生XHR的封装,除此以外还增添了对JSONP的支持。经过多年的更新维护,真的已经是非常的方便了,优点无需多言;如果是硬要举出几个缺点,那可能只有:

    • 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
    • 基于原生的XHR开发,XHR本身的架构不清晰。
    • JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
    • 不符合关注分离(Separation of Concerns)的原则
    • 配置和调用方式非常混乱,而且基于事件的异步模型不友好。

    Vue2.0之后,尤雨溪推荐大家用axios替换JQuery ajax,想必让axios进入了很多人的目光中。

    axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:

    • 从浏览器中创建 XMLHttpRequest
    • 支持 Promise API
    • 客户端支持防止CSRF
    • 提供了一些并发请求的接口(重要,方便了很多的操作)
    • 从 node.js 创建 http 请求
    • 拦截请求和响应
    • 转换请求和响应数据
    • 取消请求
    • 自动转换JSON数据

    防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key,根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。

    fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。

    总得来说:axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。


    转自:csdn 论坛 作者:布诺i

    日历

    链接

    个人资料

    蓝蓝设计的小编 http://www.lanlanwork.com

    存档