1.1 :严格界定范围和使用顺序,做用域消失变量消失,必须先声明再使用
console.log(test)//wrong let test='1';1.2 :重复定义
let test='1' let test='1'//wrong
var test='1' for(let test=1;test++;test<10)
{ alert(test)//wrong }
1.3 :let应用:闭包问题的优化
- 问题1:请思考代码执行结果 function A(){ for( a=0;a<10;a++) { ClassB=function B(){ alert(a)
} } } A() ClassB()- 结果1:弹出10次 从0到9 ? 代码执行函数定义,执行了10次定义过程,
前1次覆盖后1次的定义过程,每次循环均覆盖
内存方法区里被写入函数定义,发现同名函数会替换函数方法体
在函数调用的时候 只会执行1次alert,结果1确定是错的
- 结果2:弹出1次 9
结果1是错的,那么结果2中只弹出1次是对的
到底弹出几呢?循环9次最后9应该是9
难道是弹出9?- 结果3:undefined
得出这个答案的朋友基础知识很是棒,对函数调用和内存的问题掌握的很好:
在函数A中声明了另外的内部函数B 内部B使用了外部函数A中的变量a 咱们先思考下函数调用的问题吧 变量a位于函数A中, A函数发生调用之后 A函数出栈
A中定义的全部变量会被清空 一样变量a消失 但是当咱们调用函数B的时候须要用到a 那么a消失了因此是undefined- 正确答案:alert(10) 根据结果3得知:函数内定义了另外函数,另外函数使用了外部函数的变量
熟悉java的同窗:是否是和java的内部类和外部类很像? 在发生函数定义的时候 javaScript引擎发现内部函数在使用这个变量
该变量须要共享 ,不能存到栈中,由于栈中会被清空数据 对应于JAVA程序中 该变量会被存储到内存的方法区
和全部static变量位于同1个区域,该区域内的变量要加final关键字
变量共享之后内部函数和外部函数均操做该变量的引用
外部函数在代码执行完毕时候注意最后还有个a++ 此刻a=10;
此刻内部函数也在使用这个a(共享的a对象引用) 那么内部函数在使用a(共享的a)的时候,a(共享的a)已经被修改为10了 因此内部函数再调用,访问a的时候 弹出alert(10)- 闭包解决方案1:使用函数封装闭包代码 形式参数接受共享变量 //使用函数封装闭包代码 //那么在函数内使用的是形式参数i接受共享变量a //在发生闭包调用的时候 直接调用这个函数把共享变量传递进来i=a; //方法中的形式参数但是每次都出栈的,咱们操做的都是形式参数 //第一次循环操做的是形式参数i 此刻i=a,a是0那么i=0,方法写入方法区 //第2次循环i=a,a是1 弹出alert(i)弹出1 方法写入方法区替换 //第10次循环i=a,a是9 弹出alert(i)是9 方法写入方法区替换 //A执行完毕修改了a,a++此刻a=10 //可是方法区里的方法操做的是alert(i) 再也不是a了,写入方法区那一时刻i=9 //因此结果是alert(9) function A() { var _loop = function _loop(i) { ClassB = function B() { alert(i); }; }; for (var a = 0; a < 10; a++) { _loop(a); } } A(); ClassB();
- 闭包解决方案优化: //每次都写个函数定义形式参数好麻烦,使用let可完成上述操做 function A(){ for(let a=0;a<10;a++) { ClassB=function B(){ alert(a)
} } } A() ClassB()
1.4 普通赋值析构
[对象属性.赋值的变量]=对象 [a.A,b.B,c]=对象 获取对象的属性a 赋值给变量A b同a赋值 获取对象的属性c 没有发现要赋值的变量 自动生成同名变量c 则c=对象c属性对应的数值
1.5 参数传递析构
var func= function({a,b,c}){ console.log(a)//获取实际参数对象中的属性a赋值 没有赋值变量 生成同名变量a console.log(b) console.log(c) } func({a:1,b:2,c:3})
1.6函数调统阶段使用。。。展开
//参数展开
function sum (x, y, z) {return x+y+z } args=[0,1,2] sum(..args)//自动获取数组args中的每1个对象而且赋值
1.7 函数定义阶段使用。。。动态传参 做为数组接受全部参数
function sum(..x)
{ console.log(x) } sum(1,2,3)
1.8 赋值阶段使用:拷贝数据
var arr = [1,2,3];
var arr2 = [...arr];//展开[1,2,3]而且拷贝返回新的对象1,2,3
1.9 在箭头函数中实现代码使用{},大括号表示代码执行
(a)={alert(a)} ==== function(a){alert(a)}
1.10 箭头函数返回对象(),小括号表示对象返回
(a)={return a} ==== (a)=>(a) ===function(a) {return a}
1.11 操做this
var func=(a)={alert(this)} this是什么?区别于普通函数this再也不是函数调用这
p=new Person() p.func();//func中的this再也不是Person对象 箭头函数中的this是箭头函数声明所在类,或者所在function的对象 箭头函数在哪里声明的this就是谁 优势:节省了绑定this操做 以往在jsx中操做点击事件 要处理当前组件对象数据 须要手动bind(this)这样才能够访问到当前组件 如今只须要用箭头函数 自动绑定
1.12 链式调用
var func=(a)=>(b)=>{alert(b)}
b是什么? b是执行一次调用:func() 之后再次执行调用传递的参数 func()('this is b') 即b是用来接收第2次调用的形式参数 在发生调用的时候 第2次调用的参数赋值给b
- 优势: 使用高阶函数实现柯里化操做 再调用函数时 业务更加清晰
未柯里化: func(a,b,c) { 分别使用a,b,c处理业务 }
func(1,2,3) 柯里化 : func(a) { a处理业务 return func(b) { b处理业务 return func(c) { c处理业务 } } } func(1)(2)(3) 即把之前须要传递多个参数的函数操做
定义成只须要传递1个参数
每次操做结束后再次调用 接受下1个参数
完成之前多个参数的需求- 缺点: 自动生成了大量闭包,变量过多被共享到内存方法区里注意内存泄漏
1.13 链式调用的应用
- 实现异步操做:(也叫作thunk封装)
需求:获取数据之后再更新HTML页面
使用指定api:UpdateHtml(getData())
getData为异步ajax获取数据 UpdateHtml为更新HTML
ajax响应成功之后才会更新 如何在调用UpdateHtml此函数的时候 不更新页面
让getData再ajax获取数据成功之后在更新页面呢? //定义异步操做
Update=function(){ document.getElementById('root').innerHTML="数据更新成功" } //该UpdateHtml函数此刻已经成为异步函数 //调用它自己不会去更新dom //咱们在使用getData的时候 经过链式函数能够获取到真正的更新DOM的Update方法 //此类封装叫作thunk var UpdateHtml=(getdata)=>{ getdata()(Update)}//调用传递过来的getdata函数而且执行高阶调用,把更新dom的函数做为高阶函数的参数传递过去 //函数声明完毕开始函数调用 //如何在getData函数里获取到Update方法呢? var getData=()=>assignByUpdate=>{ //getData函数发送ajax请求更新dom //经过链式调用 声明assignByUpdate //该变量assignByUpdate被UpdateHtml第2次调用的时候传递的参数赋值 //var UpdateHtml=(getData)(Update) //assignByUpdate==Update //true ajax( success:function(){ assignByUpdate } ) } UpdateHtml(getData)- Redux里使用异步action会大量使用上述的thunk操做
1.14 定义:
给函数A标记* 函数被*标记之后表示该函数会被while(true)无限循环监控 在标记有yield的代码的地方会暂停下来 由于有while监控 因此当使用特殊指令的时候 代码才会继续执行 该函数会返回1个对象用于控制代码执行流程 使用返回对象.netx()代码才会继续执行 //发现星号标记 开启无限循环while(1)监控代码执行步骤 var work=function* A() { yield console.log(1) //发现yield标记代码会在此处中止 console.log(2) console.log(3) } work.next();//从yield出继续执行下面的代码
1.15 对比上面的thunk封装,yield更加简洁
//首先在使用yiled上的函数上加*号 //异步ajax获取数据 //标记yield的代码行被javaScript引擎解析 //代码在此处暂停,等待接受指令 var result=*function UpdateHTML(){ yiled getData(); document.getElementById('root').innerHTML="数据更新成功" } function getData(){ if('数据查询成功') { result.next();//发出指令 容许返回result标记*号的方法继续从yield处执行 //即UpdateHTML()从yield标记开始继续执行了 } }
1.16 yield应用:redux saga
如下代码为redux saga示例 经过*和yield来控制代码执行流程 缺点:while无限循环影响性能 import { call, put } from 'redux-saga/effects' export function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.url); yield put({type: "FETCH_SUCCEEDED", data}); } catch (error) { yield put({type: "FETCH_FAILED", error}); } }
1.17 export:变量导出 不一样位置js文件变量交互
在1.js中定义了2个变量 var name='李雷' var age="15" 在2.js中如何使用这2个定义好的变量呢? 咱们只须要在1.js中 var name='李雷' var age="12" //添加以下代码 export {name,age} 在2.js中使用就可使用了
1.18 import:使用导出的变量
import {name,age} from "./1.js" console.log(name) //'李雷' console.log(age)//12
1.19 变量导出导入的方案:
普通导出export {name,age} -- 使用的时候必须指定name和age:即import {name,age} from "./1.js" -- 若是在2.js中使用的时候不想使用name和age,想用your1_name或your_age2来接受 1.js中的2个变量怎么办? -- 使用默认导出 默认导出 export default 只支持一个变量导出 export default name -- 使用的时候 import any from "./1.js" -- any能够任意起名 -- console.log(any)//李雷 批量导出1.js -- export const name = '李雷' -- export const age = '12' 批量导入2.js -- import * as types from '1.js' 使用types对象封装1.js中全部export的变量 export中变量的定义名字是types对象的属性 export中改变量的对应的数值是types对象的改属性的数值 console.log(type)//{name:'李雷',age:'12' }
1.20 变量导出导入的方案:
在数据状态变化的时候,去执行预先定义好的方法 //定义好了异步对象 分别封装了2种状态 //异步函数须要传递个回调函数做为状态切换方法 //该回调函数中2个参数resolve, reject //resolve是resolve(data)用于切换resolve状态 //reject是reject(data)用于切换reject状态 //data为两种状态切换时传递的参数 var notify=new Promise(function (resolve, reject) { if (ready) { resolve("请求成功"); } else { reject("网络出错"); } //若是对象状态发生切换,即手动调用了resolve(data),reject(data) //会触发promis对象的then中定义的对应的回调方法,即参数1函数和参数2函数 nofity.then(func1(data){alert(data)//会显示"请求成功"},func2(data)//会显示"请求失败")
1.21 代理模式概述
代理对象调用本身的一切方法时候均会被拦截从而实现业务操做 静态代理:被代理的原始类方法发生变化须要手动修改地代理里方法 动态代理:根据被代理的原始类自动生成代理类对象, 原始类方法变化 代理对象按照原始类生成也随着变化 不用手动修改,java中spring aop中用的基本都是asm动态代理
1.22 代理模式应用
代码中有10个service类 ,之前的业务是不涉及到事务操做 如今需求是在service中查询数据库的时候开启事务和关闭事务 那么若是没有代理模式 须要操做10个类文件一步一步改 为了不在每一个service对象中均开启事务 关闭事务 咱们能够把原始的service对象修改为代理对象, 那么在代理对象中查询数据库 等相关操做均会被拦截 从而开启事务 关闭事务 咱们仅仅在1个类中修改 就能够完成之前在全部service对象中修改的效果
1.23 代理模式ES6应用
const target = {
name: '李雷', age: 15 }; let handler = { //获取被代理对象的属性会调用 get(target, key, proxy) { //容许方法继续执行下去 return Reflect.get(target, key, proxy); } } //构建代理对象 const proxy = new Proxy(target, handler);
1.24 arrayObject.map(function(i){})
//迭代处理数组中的每一个元素i是迭代器,即每次迭代的对象1.25 arrayObject.reduce(回调函数(p,c){},初始数值)
//若是指定了初始数值 则第一次迭代的p返回数值就是这个初始数值 //若是没有指定初始数值,则第一次的带返回的p默认是0 //c是当前指针位置元素 //[1,2,3].reduce(function(p,c){return p+c},0) // 第一次迭代函数返回0,因此p=0,指针指向0号位置数值是1因此c=1 // 第二次迭代p=上次的返回数值0+1 c是2 //依次类推0+1+2+3是结果 arrayObject.reduce(回调函数(p,c){},初始数值) //find,filter api相似map
#类和属性和方法定义java
1.26 使用Class定义类
class Person { }
1.27 定义属性:经过构造器定义属性,区别于java的成员变量
class Person { constructor(name) { this.name = name;//定义属性name this.age=12 ;//定义属性age 这点和java有所区分 不须要成员变量 } }
1.28 定义类方法:方法函数不要加function
class Person { constructor(name) { this.name = name;//定义属性name this.age=12 ;//定义属性age 这点和java有所区分 不须要成员变量 } say(){ console.log("hello");//类中方法千万不要加function } }
switch (i) { case 0: let a=0; break; case 1: let a=1; // TypeError for redeclaration. break; } 由于switch做用域内禁止出现同名let变量 即便break,在代码编译时会失效
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)ajax