和taro一起做SPA 1.基础知识

这一章的主要目的是和大家一起复习一下taro涉及的基础知识,为后面的学习做好基础

1. 基础知识

1.1 this指针

this指针大概是javascript中最令初学者困惑的语法了,简单说,this指针就是指向函数或方法运行的上下文环境。既然叫上下文环境,肯定和运行的环境相关。
在浏览器环境下:

  • 当this出现在函数调用中,指向的是他运行的上下文:window对象;
  • 当this出现在对象方法调用时,则指向了他运行的上下文:对象本身;
    让我们举个例子说明一下:
    function fn(){
        console.log(this);
    }
    var obj={
        fn:fn
    }
    fn();
    obj.fn();

通过运行代码,你会发现,fn返回的是window对象,而obj.fn返回的则是obj对象
为了加深理解,我们看一个有点迷惑的问题



    

这是一个常见的场景,每次使用document.getElementById方法很麻烦,所以重新定义一个函数 getId,指向document.getElementById方法.
但是你发现,在chrome浏览器中调用getId方法居然系统会抛一个异常:

test.htm:9 Uncaught TypeError: Illegal invocation 

发生异常的原因就在于,getElementById内部实现使用了this指针指向document对象.但是,当你用自己定义的getId方法时,getElementById已经由对象调用变成了方法调用,this指针被指向了window对象.

既然this指针有这样的不确定性,那么,自然就可以想到如何根据需要变更他的指向。
变更this指针指向有两种方法,定义时绑定和运行时绑定。

  • 定义时绑定 bind
  • 运行时绑定 apply/call
    还是让我们看个例子:
    对于我们上个getId的例子,你只要这样调用就可以了
    定义时通过bind方法绑定
    const getId=document.getElementById.bind(document);
    const e=getId("root");

运行时通过call方法绑定

    const e=getId.call(document,"root")

运行时通过apply方法绑定

     const e=getId.apply(document,["root"])

通过上面的例子也可以看到call和apply两者的区别是:apply绑定时,传入的参数为数组形式,而call绑定则是采用枚举的方式。所以如果getId方法需要使用apply方法时,必须将参数包装成数组的形式.

1.2 高阶函数

在javascript语言中,函数是一类成员,函数可以作为变量,也可以作为输入参数和返回参数.将函数作为输入参数或输出参数的函数称之为高阶函数.后面我将会带大家一起了解一下高阶函数.

1.2.1 闭包

让我们看一下第一个高阶函数,闭包.
闭包利用了javascript函数作用域内变量被引用不会消除的特性.闭包被应用的场景非常多.
让我们先看一个获取递增ID的例子,首先,让我们看一下传统的方法,传统的方法获取递增ID,你需要先做一个全局变量.

let globalID=0
function getID(){
    return globalID++;
}
console.log(getID(),getID(),getID())

这种方法由于使用了全局变量,任何一个人都有可能不经意的修改globalID的值导致你的方法失效.
采用闭包的写法,你先创建一个crGetID方法通过闭包保存ID,并返回getID函数.然后通过getID方法获取ID

function crGetID(){
    let id=0;
    return function(){
        return id++;
    }
}
var getID=crGetID();
console.log(getID(),getID(),getID())

这样,没有人可以直接修改你的ID值.

1.2.2 currying

让我们再看一个闭包的应用:currying,currying解决的问题是把一个函数的多个参数转换为单参数函数.
举个例子,假设我们需要累计一个用户7天的数据:

function add(d1,d2,d3,d4,d5,d6,d7){
    return d1+d2+d3+d4+d5+d6+d7
}

如果是30天,可能需要30个输入参数,如果不定天数呢?
采用currying则可以解决这个问题:

function curryAdd(){
    let s=[];
    return function(...arg){
        if (arg.length==0){
            return s.reduce(function(p,v){return p+v},0)
        }else{
            s.push(...arg)
            console.log("s",s);
        }
    }
}
var ca=curryAdd();
ca(1);
ca(2);
ca(3);
ca(4);
console.log(ca());

通过将一个函数currying后,函数可以随时被调用,直到输入参数为空时才进行计算.
闭包的特性使之成为javascript中运用最广的特性.后续在代码中,我们还会继续看到大量的闭包用法.

1.3 es6语法

react 大量使用了es6的语法,如果你对javsascript的印象还停留在原始的印象里,你可能根本没法看懂react的代码。所以在这里我们简单对用到的es6语法做一些介绍,并尽可能以react实际使用作为学习的例子。详细的es6语法介绍,可以参考相关的技术文档。

1.3.1变量解析

  • 数组变量解析
    es6 支持对数组直接进行解析,举个例子,如果需要对变量x,y互换值,传统的做法是:
function(x,y){
    var t;
    t=x;
    x=y;
    y=t;
}

如果用数组解析,就容易多了:

let [x,y]=[y,x]

数组解析可以用到输入参数传递

    function test ([x,y,z]){
        return x+y+z;
    }
    console.log(test([1,2,3]))

上面的例子,打印出来的结果是6.
还可以用到一次返回多个参数:

    function retMult(){
        return [1,2,3]
    }
    let [x,y,z]=retMult();
    console.log(x,y,z)

函数会打印出 1,2,3

  • 对象变量解析
    对象解析用的更广泛,让我们举个简单的例子:
    let {x:x,y:y}={x:2,y:3}
    console.log(x,y);

函数打印结果 2,3
还可以简写为:

    let {x,y}={x:2,y:3}
    console.log(x,y);

让我们看一下下面的例子,如果对象属性名称和变量名称可以更进一步进行简写:

    let x=1;
    let obj={x};  //相当于 obj={x:x}
    console.log(obj.x);

和数组解析一样,对象解析大量应用在输入参数的传值上,让我们举一个redux的实际应用的例子(这个例子里,假设state对象仅包含一个value属性):

function  reducer({value},{type,payLoader}){
    switch(type){
        case "calc":
            return  {value:value+payLoader}
        default:
            return {value}
    }
}

这个例子里,使用了对象的解析,代码更加简洁和异动.如果不使用对象解析,你的代码是这样的:

function reducer(state,action){
    switch(action.type){
        case "calc":
            return {value:state.value+action.payLoader}
        default:
            return state;
    }
}

1.3.2箭头函数

箭头函数可以让代码更加简洁和直观,另外,由于箭头函数对this指针的特殊处理,因此,被大量的运用。
让我们还是以数组提供的map函数为例子说明:

let arr=[1,2,3,4];
let m=arr.map(v=>v*2);

使用箭头函数,即简洁又直观,对数组中的每个元素直接乘以2.
如果使用传统的方式,你需要这样写:

let n=arr.map(function(v){
    return v*2;
})
console.log(n);

使用箭头函数需要注意以下几点:

  • 如果是一个参数,可以省略(),如果多个参数或没有参数,则必须使用()
  • 如果函数直接返回箭头后的表达式,可以不加{},否则,需要在箭头后使用{}
    让我们再举个数组提供的reduce方法的实际例子:
var r=[1,2,3,4].reduce((p,n)=>p+n);
console.log(r);

这个例子通过reduce函数计算数字元素的累加和,reduce函数的输入参数是一个函数,函数的输入参数分别为累加之和p以及下一个元素n.
如果用传统的方法,你需要这样写:

var r=[1,2,3,4].reduce(function(p,n){
    return p+n;
})
console.log(r);

1.3.3类

ES6提供了类,类实际上就是一个语法糖.让我们还是通过一个具体的例子来看一下类的实现:

class Counter extends Component{
    constructor(props){
        super(props)
    }
    sub(){
        let value=this.state.value-1;
        this.setState({value})
    }
    add(){
        let value=this.state.value+1;
        this.setState({value})
    }
    render(){
        return (
            
) } }

这个例子说明了ES6的类的用法.
首先,类的定义语法是:

class  Name{
}

你的类可以继承自另一个类,在刚才的例子里我们的类Counter继承自React的Component类

class Counter extends Component{
}

你可以根据需要实现类的构建器,构建器可以通过super方法引用父类的构建器:

    constructor(props){
        super(props)
    }

类的方法可以缩写为methodName(){}的形式

    sub(){
        let value=this.state.value-1;
        this.setState({value})
    }

你可能感兴趣的:(和taro一起做SPA 1.基础知识)