JavaScript基础

67 阅读6分钟

数组

  • 数组的两种声明方式

    var arr1 = new Array(1, 'a', false)
    var arr2 = [2, 'b', true]
    
  • 越界不报错

    console.log(arr1[5]) // undefined 
    
    
  • 索引赋值,索引超过范围照样能赋值

    arr1[5] = null
    console.log(arr1) // [ 1, 'a', false, <2 empty items>, null ]
    
  • push pop向数组的末尾(添加、删除元素)

    var s = arr1.pop()
    console.log(s)
    arr1.push(1, 2)
    
  • unshift shift 向数组的头部(添加、删除元素)

    arr1.unshift('a', 1)
    console.log(arr1.shift())
    
  • splice (start: number, deleteCount: number, ...items: string[])

    • 从start开始删除 deletecount个参数, items为在删除后在start位置添加的参数
    var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
    // 从索引2开始删除3个元素,然后再添加两个元素:
    arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
    
  • sort/reverse 对arr进行排序 正向和反向

    console.log(arr.sort())
    console.log(arr.reverse())
    
  • concat 和slice 将数组拼接,拆分

    console.log(arr1.concat(arr2))
    console.log(arr1.slice()) //arr1的浅拷贝
    console.log(arr1.slice(1, 3))
    

对象

  • 对象的创建方法

    var obj1 = new Object()
    var obj2 = {}
    
  • 未定义的属性不报错

    obj1.a = 1
    console.log(obj1.a)
    console.log(obj1.b) // undefined
    
  • 删除不存在的属性不会报错

    delete obj1.b
    
  • 判断对象是否有该属性

    console.log(obj1.hasOwnProperty('a'))
    
  • null 为空值, undefined为未定义 没什么用处

运算符

  • 逻辑运算符
    • &&与 ||或 !非
    var a = 1
    if(a == 1 && a == 2){
        console.log("a")
    }
    
  • 关系运算符
    // 关系运算符
    var b = true
    console.log(a == b) //自动转换类型比较
    console.log(a === b) //数据类型不一致返回false, 一致再比较
    

变量

  • 变量提升

    • 全局变量无论声明在何处,都会被提升至所在作用域顶部
    • JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有声明的变量“提升”到函数顶部
    {  
        let a1 = 2  //局部变量声明,无变量提升
        var a2 = 3  //全局变量声明,有变量提升
    }
    // console.log(a1) 报错
    console.log(a2)
    
    function foo1() {
        var x = 'Hello, ' + y;
        console.log(x);
        var y = 'Bob';
    }
    
    // JavaScript引擎看到的代码相当于
    function foo2() {
        var y; // 提升变量y的申明,此时y为undefined
        var x = 'Hello, ' + y;
        console.log(x);
        y = 'Bob';
    }
    
  • 常量声明

    const c1 = 20
    
  • 解构赋值

    var arr = ['hello', ['js', 'ES6']]
    var [x, [y, z]] = arr
    console.log(x, y, z)
    // 相比于传统方式简单不少
    x = arr[0]
    y = arr[1][0]
    z = arr[1][1]
    console.log(x, y, z)
    
    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
    }
    
    // 对象解开后是属性, 可以直接导出你需要的属性, 用的地方很多
    var {name, age, passport} = person
    console.log(name, age, passport)
    

异常捕获

  • try-catch-finally
    • try: 捕获代码块中的异常
    • catch: 出现异常时需要执行的语句块
    • finally: 无论成功还是失败 都需要执行的代码块
    var s = null
    
    try{
      s.length
    }catch(e){  // e为捕获到的Error,可自己命名
        console.log('has error, '+ e)
    }finally{
        console.log('final')
    }
    
  • Error对象 与抛出异常
    var err =  new Error('异常')
    console.log(err)
    // Error: 异常
    //     at file:///d:/%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99/%E5%89%8D%E7%AB%AF/err.mjs:16:12
    //     at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
    
    try{
        throw err
    }catch(e){
        console.log('has error, '+ e)
    }
    

函数

function add(x, y){
    return x + y
}

console.log(add(1, 2))
console.log(add('a', 2))
  • 对象添加方法
    var person = {name:'小明', age:23}
    person.Name = function(){
        console.log(`${this.name}`)  // this为本对象
    }
    
    person.Name()
    
  • this的坑:返回的函数找不到上下文(this)
    person.greetfn = function() {
        return function() {
            console.log(`hello, my name is ${this.name}`)
        }
    }
    person.greetfn()()
    
  • 解决方法:通过变量+闭包把this传递
    person.greetfn = function() {
        var that = this  // 这个写法很多,别看不懂
        return function() {
            console.log(`hello, my name is ${that.name}`)
        }
    }
    
  • 箭头函数(匿名函数)。 实体为: (params ...) => { ... }
    var fn = x => x*x
    console.log(fn(2))
    
  • 箭头函数的this可继承上下文
    person.greetfn = function() {
        return () => {
            // this 继承自上层的this
            console.log(`hello, my name is ${this.name}`)
        }
    }
    

名称空间

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象 window ,全局作用域的变量实际上被绑定到 window 的一个属性
浏览器内使用 alert("1")window.alert("1") 相同

  • 覆盖浏览器内置方法 alert image.png
    修改后

image.png

  • 导出变量
    //export.mjs文件
     var firstName = 'Michael';
     var lastName = 'Jackson';
     var year = 1958;
    
    export {firstName, lastName, year};
    
    //import.mjs文件
    import { firstName, lastName as ln, MYAPP, year } from "./export.mjs" //as能进行重命名
    console.log(firstName, ln)
    
  • 将导出的属性和方法绑定到一个变量
    //export.mjs文件
    var MYAPP = {}
    MYAPP.name = 'myapp';
    MYAPP.version = 1.0;
    
    MYAPP.foo = function () {
        return 'foo';
    };
    
    export {MYAPP};
    
    //import.mjs文件
    import {MYAPP as Module}from'./export.mjs'
    console.log(Module.name)
    
  • 默认导出
    • 使用 import 命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载, 比如
    • 如果我们想要直接导入模块,再看该模块下有哪些变量可用
    // export default命令用于指定模块的默认输出。
    //显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。
    //所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令
    
    //export.mjs文件
    var APP = {}
    APP.name = 'APP'
    export default APP
    
    //import.mjs文件
    import myPKG from './export.mjs'
    console.log(myPKG.name)
    

条件与循环

  • if语句和for语句与c语言相同

  • for in 对象遍历出来的元素为对象的属性, 数组遍历的元素为索引

    var o = {
        name: 'Jack',
        age: 20,
    };
    for (var key in o) {
        console.log(key); // 'name', 'age'
    }
    
  • for of 遍历数组元素,不能遍历对象(可有变通方法)

    var a = ['A', 'B', 'C'];
    for (var x of a) {
        console.log(x); // 'A', 'B', 'C'
    }
    
    for (var key of Object.keys(o)){ // 变通方法
    console.log(key)
    } 
    
  • forEach 相当于对for of的面向对象化

    a.forEach(element => {
        console.log(element)
    })
    

Promise对象

  • 在 JavaScript 的世界中,所有代码都是单线程执行的。
  • 由于这个“缺陷”,导致 JavaScript 的所有网络操作,浏览器事件,都必须是异步执行。Javascript 通过回调函数实现异步

单线程异步模型

  • 回调:并不会真正阻塞1秒, 而是在1秒后调用该函数, 这就是 JavaScript 的编程范式: 基于回调的异步。
    function callback(){
        console.log('Done')
    }
    
    console.log('before setTimeout()');
    setTimeout(callback, 1000); // 1秒钟后调用callback函数
    console.log('after setTimeout()');
    

Promise与异步

  • 我们来看一个函数, resolve 是成功后的回调函数, reject 是失败后的回调函数

    function testResultCallBackFunc(resolve, reject){
        var timeOut = Math.random() * 2;
        console.log('set timeout to :' + timeOut + 'seconds.')
        setTimeout(function(){
            if(timeOut < 1){
                console.log('call resolve()...');
                resolve('200 ok');
            }else{
                console.log('call reject()...')
                reject('timeout in' + timeOut + 'seconds.')
            }
        }, timeOut * 100);
    }
    
  • 然后我们把处理成功和失败的函数 作为回调传递给该函数

    function testResultCallBack(){
        var success = (message)=>{console.log(`success ${message}`)}
        var failed = (error)=>{console.log(`failed ${error}`)}
        testResultCallBackFunc(success, failed)
    }
    
    testResultCallBack()
    // set timeout to: 0.059809346310547795 seconds.
    // call resolve()...
    // success 200 OK
    
  • 可以看出, testResultCallbackFunc() 函数只关心自身的逻辑,并不关心具体的 resolvereject 将如何处理结果

  • JS 把这种编程方式抽象成了一种对象: Promise

    var p1 = new Promise(testResultCallbackFunc)
    p1.then((resp) => {
        console.log(resp)
    }).catch((err) => {
        console.log(err)
    })
    // set timeout to: 0.628561731809246 seconds.
    // call resolve()...
    // 200 OK
    
  • 可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了: image.png

  • Async函数和promise组合

    • 这些需要提到 async函数, async函数由内置执行器进行执行, 这和go func() 有异曲同工之妙
    • 那我们如果声明一个异步函数, 其实很简单 在你函数前面加上一个 async 关键字就可以了
    • 这里testWithAsync就是一个异步函数, 他执行的时候 是交给 JS 的携程执行器处理的, 而 await 关键字 就是 告诉执行器 当 p1 执行完成后 主动通知我(协程的一种实现)
    async function testWithAsync(){
        var p1 = new Promise(testResultCallBackFunc)
        try{
            var resp = await p1
            console.log(resp)
        }catch(e){
            console.log(e)
        }
    }