javascript 基础语法教程

javascript 基础语法教程

JavaScript let和var

在JavaScript中,letvar 都是用于声明变量的关键字,但它们之间有一些重要的区别。

作用域

  • var : 具有函数作用域或全局作用域。在函数内部声明的 var 变量在整个函数内部都可见。在全局作用域中声明的 var 变量将成为全局对象的属性(在浏览器中是 window 对象)。
  • let : 具有块作用域。在 {} 块内声明的 let 变量只在该块内可见。不会在外部作用域中泄漏。
function print(){
    {
        var a=10;
        let b=20;
    }
    console.log(a);
    console.log(b);
}

在上面的代码中,变量a能正确输出,但是变量b会输出 ReferenceError: b is not defined 。因为变量a使用 var 声明,在声明后会被提升到函数作用域,在整个函数内部都是可见的。变量b使用 let 声明,因此其作用域为声明变量的 {} 内,所以在后面的函数中输出时会报错。

隐式全局变量

在 JavaScript 中,即使没有显式声明变量,也可以直接赋值给一个变量,这被称为 隐式全局变量 。对于如下代码,执行后能正常输出数字10.

a = 10;
console.log(a); // 输出 10

在js中,当在全局作用域中直接赋值给一个未声明的变量时,JavaScript 引擎会自动在全局对象(在浏览器中是 window 对象)上创建一个属性。

因此,a = 10; 实际上等价于 window.a = 10;

变量提升

  • var : 会被提升到其作用域的顶部。即使在声明之前使用 var 变量也不会报错,但变量的值会是 undefined
  • let : 也会被提升,但不会被初始化。在声明之前使用 let 变量会导致引用错误(ReferenceError),因为它们处于“暂时性死区”(Temporal Dead Zone, TDZ)。
console.log(a);
var a = 10;

上面的代码能正常执行,但是打印结果为 undefined 。因为在执行时,变量a会被提升到作用域顶部,此时作用域内存在变量a的定义,但是变量提升只会提升变量定义,不提升变量赋值,因此最后变量a的值为 undefined 。上面的代码等价于:

var a;
console.log(a);
a = 10;

将上面的定义方式修改为 let 之后,会出现 ReferenceError: Cannot access 'a' before initialization 。let变量同样存在变量提升,但它们会在声明之前处于一个“暂时性死区”,在这个区域内访问变量会导致引用错误。

重复声明

  • var : 允许在同一作用域内多次声明同一个变量。
var x = 10;
var x = 20; // 不会报错,但会覆盖之前的值
  • let : 不允许在同一作用域内多次声明同一个变量。
let y = 10;
let y = 20; // 报错:Identifier 'y' has already been declared

JavaScript null和undefined

在JavaScript中,undefinednull 都表示“无”或“空”的概念,但它们在语义和用途上有明显的区别。

  • undefined
    • 通常用于表示一个变量已被声明但尚未赋值。
    • 函数没有返回值时,默认返回 undefined
    • 对象属性或数组元素不存在时,默认值为 undefined
  • null
    • 通常用于表示一个有意的空值或对象的缺失。
    • 用于清空对象引用,表示对象不再需要。

简单地说,undefined 表示一个对象已经被定义,但是在使用时还没有为这个对象赋值。null 表示对象已经完成了定义和赋值,只是对象的值为空。

JavaScript 字符串模板

JavaScript 中的模板字符串是一种方便的字符串语法,允许你在字符串中嵌入表达式和变量。其基本语法是使用一组 定义一个字符串,在其中可以使用特定格式插入变量或函数。同时,字符串模板中可以出现单引号和双引号,并且不需要转义,但是对于其中出现的 则需要使用 \ 进行转义

语法

// 基本用法
let text1 = `Hello RUNOOB!`;

// 转义字符
let text2 = `Hello\` RUNOOB!`;

// 无需转义
let text3 = `Hello "RUNOOB" '!'`;

// 模板字符串
let name = "zhangsan";
let info = `name: ${name}`;

// 函数
function sum(a, b) {
  return a + b;
}
let result = `1+2=${sum(1, 2)}`;

// 多行文本
const multiLineText = `
  This is
  a multi-line
  text.
`;

JavaScript this关键字

面向对象语言中 this 表示当前对象的一个引用。但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
let person = {
    name: 'John',
    age: 30,
    city: 'New York',

    print: function () {
        console.log(`Name: ${this.name}, Age: ${this.age}, City: ${this.city}`);
    }
}
person.print();

Name: John, Age: 30, City: New York

在方法中,如果使用箭头函数定义方法,则方法中this指向的是定义 person 对象时的外层作用域,通常是全局对象。在浏览器中,这个对象通常是window。

如果要在对象中使用箭头函数定义方法,且方法中this指向对象中的属性,则可以使用闭包实现。

let person = {
    name: 'John',
    age: 30,
    city: 'New York',

    print: function () {
        console.log(`Name: ${this.name}, Age: ${this.age}, City: ${this.city}`);
    },

    print2: () => {
        console.log(`Name: ${this.name}, Age: ${this.age}, City: ${this.city}`);
    },
    print3: function () {
        const self = this;
        return () => {
            console.log(`Name: ${self.name}, Age: ${self.age}, City: ${self.city}`);
        }
    }
}

person.print();
person.print2();
person.print3()();

输出结果:

Name: John, Age: 30, City: New York
Name: undefined, Age: undefined, City: undefined
Name: John, Age: 30, City: New York

  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
// 直接声明变量,会绑定到全局作用域
name = 'John';
age = 30;
city = 'New York';
function print() {
    console.log(`Name: ${this.name}, Age: ${this.age}, City: ${this.city}`);
}

print();
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
let person = {
    name: 'John',
    age: 30,
    city: 'New York',

    print: function () {
        console.log(`Name: ${this.name}, Age: ${this.age}, City: ${this.city}`);
    }
}
let person2 = {
    name: 'zhangsan',
    age: 10,
    city: 'bj',
}
person.print.call(person2);

JavaScript call()和apply()

在JavaScript中,call 和 apply 方法都用于调用函数,并且可以指定函数内部的 this 值。这两个方法的主要区别在于传递参数的方式不同。

call()

  • 作用: call调用一个函数时,可以指定函数内部的this并向函数中传递参数。其中,参数以单个的形式传递到函数中
  • 语法: myFunction.call(otherThis, "arg1", "arg2", ...)

apply()

  • 作用: apply调用一个函数时,同样可以指定函数内部的this并向函数中传递参数,和call调用的区别在于,这里的参数是以数组的形式传递的。
  • 语法: myFunction.apply(otherThis, ["arg1", "arg2", ...])
let person = {
    name: 'John',

    print: function (age, city) {
        console.log(`Name: ${this.name}, Age: ${age}, City: ${city}`);
    }
}
let person2 = {
    name: 'zhangsan',
}
person.print.call(person2, );
person.print.apply(person2, [40, 'beijing']);

JavaScript 异步编程

setTimeout 函数

setTimeout() 函数接收两个参数:一个回调函数 callback 和一个延迟执行时间 delay 。其作用为,在触发 setTimeout() 函数之后,在经过 delay 毫秒之后将 callback 函数放到事件队列中,等待事件循环调用该函数。

function print() {
    console.log("print function");
}

function myFunction() {
    setTimeout(print, 3000);
    dosmething() // 耗时 5000ms
    console.log("hello world");
}

myFunction();

假设存在如上代码,这里实际打印结果为 hello world - print function 。程序执行到setTimeout之后,会等待3000ms延时,之后将 print 函数放到事件队列中等待执行,而在执行setTimeout的同时,主线程不会等待延时事件之后执行,而是立即执行 myFunction 函数后面的代码。

类似文章