一句话理解JavaScript基础知识点

JavaScript基础知识概念,尝试尽量用最精简的话表达。

1. 原型

除null外的js对象,在创建的时候会与之关联另外一个对象,这个对象就是原型,每个对象都会从原型继承属性。

  • proto: 每个除null之外的对象都具有一个__proto__属性,这个属性指向该对象的原型。
  • constructor: 每个原型都有一个constructor属性指向关联的构造函数

加个例子理解:

function Person() {
}
var person = new Person();

person.__proto__ === Person.prototype
Person === Person.prototype.constructor
Object.getPrototypeOf(person) === Person.prototype

2. 原型链

当读取实例属性时,如果找不到就会查找对象关联的原型中的属性,如果还找不到,就会去找原型的原型,一直找到最顶层为止。这个由相互关联的原型组成的链状结构就是原型链。

最顶层就是原型链的最后一个,即Object.prototype的原型,值为null

Object.prototype.__proto__ = nullObject.prototype 没有原型,所以查找属性的时候查到 Object.prototype 就可以停止查找了

3. js运行时

js运行时有两个阶段:编译阶段和执行阶段。

  • 编译阶段:js通过编译生成执行上下文可执行代码
  • 执行阶段:执行可执行代码,生成结果
  • 可执行代码 就三种,全局代码、函数代码、eval代码
    当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做”执行上下文(execution context)

4. 执行上下文

js代码执行一段代码时的运行环境,比如调用一个函数,就会进入到这个函数的执行上下文,确定函数在执行期间用到的诸如this、变量、对象以及函数等。

执行上下文有三个重要属性:变量对象、作用域链、this

当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。

5. 变量对象(VO)

变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。

  • 全局上下文:全局上下文中的变量对象就是全局对象
  • 函数上下文: 在函数上下文中,用活动对象(activation object, AO)来表示变量对象
  • 活动对象(AO): 活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。

活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object 呐,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。

  • 函数上下文的变量对象初始化只包括 Arguments 对象

  • 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值

  • 在代码执行阶段,会再次修改变量对象的属性值

6. 作用域

程序源代码中定义变量的区域。

作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

  • 静态作用域
    即词法作用域,函数的作用域在函数定义的时候就决定了

  • 动态作用域
    函数的作用域是在函数调用的时候才决定的

7. 作用域链

当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

8. 闭包

MDN解释:闭包是指那些能够访问自由变量的函数

  • 自由变量:自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量

ECMAScript中,闭包指的是:

  • 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
  • 从实践角度:以下函数才算是闭包:
  • 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
  • 在代码中引用了自由变量

9. call、apply、bind

call和apply共同点

  • 都能够改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行。
  • 调用 call 和 apply 的对象,必须是一个函数 Function。
  • 第一个参数是一个对象,Function的调用者,将会指向这个对象,如不传,则默认全局对象window

call和apply区别:

call,第二个参数开始,可接受任意个参数,apply第二个参数是一个数组或类数组,apply只有俩参数

bind 方法 与 apply 和 call 比较类似,也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。

10. 类数组对象

顾名思义,就是具备与数组特征类似的对象。可以通过角标进行调用,具有length属性,同时也可以通过 for 循环进行遍历,无法使用 forEach、splice、push 等数组原型链上的方法

拥有一个 length 属性和若干索引属性的对象

Arguments 对象就是一个类数组对象。在客户端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也返回类数组对象

var array = ['name', 'age', 'sex'];

var arrayLike = {
    0: 'name',
    1: 'age',
    2: 'sex',
    length: 3
}
  • 调用数组方法
    用 Function.call 间接调用
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }

Array.prototype.join.call(arrayLike, '&'); // name&age&sex

Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 
// slice可以做到类数组转数组

Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
}); 
// ["NAME", "AGE", "SEX"]

Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] 

Array.from(arrayLike); // ["name", "age", "sex"] 

Array.prototype.concat.apply([], arrayLike)

10.1. Arguments对象

Arguments 对象只定义在函数体中,包括了函数的参数和其他属性。在函数体中,arguments 指代该函数的 Arguments 对象。

类数组的索引属性和length属性之外,还有一个callee属性

  • length属性
    Arguments对象的length属性,表示实参的长度
function foo(b, c, d){
    console.log("实参的长度为:" + arguments.length)
}

console.log("形参的长度为:" + foo.length)

foo(1)

// 形参的长度为:3
// 实参的长度为:1
  • callee属性
    Arguments 对象的 callee 属性,通过它可以调用函数自身。
var data = [];

for (var i = 0; i < 3; i++) {
    (data[i] = function () {
       console.log(arguments.callee.i) 
    }).i = i;
}

data[0]();
data[1]();
data[2]();

// 0
// 1
// 2

非严格模式下,传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享。在严格模式下,实参和 arguments 是不会共享的

  • 传递参数
    将参数从一个函数传递到另一个函数
// 使用 apply 将 foo 的参数传递给 bar
function foo() {
    bar.apply(this, arguments);
}
function bar(a, b, c) {
   console.log(a, b, c);
}

foo(1, 2, 3)
  • 使用ES6的 … 运算符,我们可以轻松转成数组
function func(...arguments) {
    console.log(arguments); // [1, 2, 3]
}

func(1, 2, 3);

11. 防抖和节流

防抖和节流的作用都是防止函数多次调用。

区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,

  • 防抖的情况下只会调用一次
  • 节流的情况会每隔一定时间(参数wait)调用函数

12. 传递参数

  • 按值传递
var value = 1;
function foo(v) {
   v = 2;
   console.log(v); //2
}
foo(value);
console.log(value) // 1
  • 引用传递
    传递对象的引用
var obj = {
   value: 1
};
function foo(o) {
   o.value = 2;
   console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
  • 共享传递
    传递对象的引用的副本
var obj = {
   value: 1
};
function foo(o) {
   o = 2;
   console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以第二个和第三个例子其实都是按共享传递

参数如果是基本类型是按值传递,如果是引用类型按共享传递
但是因为拷贝副本也是一种值的拷贝,所以在《高程》中也直接认为是按值传递了

13. new关键字

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 chaoyumail@126.com

×

喜欢就点赞,疼爱就打赏