关于 js



对 js 的知识梳理与重点总结。

作用域

全局作用域

  1. 全局作用域只有一个,且只有一个全局对象 window
  2. 创建的变量作为 window 的属性使用,创建的函数作为 window 的方法使用
  3. 全局作用域中的变量都是全局变量,在页面任意部位都可以访问到
  4. 全局变量只有在页面关闭时才会释放内存,否则会一直占据空间,消耗内存

局部作用域(函数作用域)

  1. 在全局作用域中定义的其他作用域都被称为局部作用域
  2. 局部作用域由函数创建,每个函数都会创建一个局部作用域
  3. 在函数作用域中可以访问到全局作用域中的变量,在全局作用域中访问不到局部作用域中的变量
  4. 局部变量会在函数调用完毕后自动释放内存

预解析

  1. 预解析就是把带有关键字声明的变量或函数进行提前声明或赋值。
  2. 预解析只能发生在当前作用域下。

变量

  1. 使用 var 声明的变量,会在当前作用域所有代码执行之前被声明,但不会赋值,若提前调用,会返回 undefined
  2. 在全局作用域中没有 var 声明的变量,该变量就不会被提前声明,若要提前调用,会报错
  3. 在函数作用域中没有 var 声明的变量,会变为隐式全局变量

函数

  1. 使用 函数声明 的形式创建的函数 function foo ( ) { },会被提前声明,并且可以提前调用
  2. 使用函数表达式创建的函数 var foo = function ( ) { } ,不会被提前声明,所以也不能在声明前调用

递归

递归函数是在一个函数通过名字调用自身的情况下构成的,递归一定要由结束条件。

1
2
3
4
5
6
7
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}

上面这个递归函数是比较经典的,但是我们改变原始函数的指向后就会发生报错。

1
2
3
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));

这里将 factorial 函数赋值给变量 anotherFactorial ,然后再将其设置为 null,那么在接下来的调用中就自然会报错。这里可以使用函数表达式的形式创建该递归函数来解决。

1
2
3
4
5
6
7
var factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
})

这样的话我们通过函数声明的方式创建了一个名为 f( ) 的函数,然后将它赋值给变量 factorial,这样即使函数被重新赋值,函数 f( ) 仍然有效,而且这种方法在严格模式和非严格模式都行得通。

闭包

什么是闭包?

闭包简单理解就是一个有权访问另一个函数作用域中的变量和数据的函数,简单的例子就是被嵌套的内部函数调用外部函数中的数据。

作用

  1. 缓存数据,让函数外部可以操作函数内部的数据 —— 局部变量一般在调用完毕后都会消失,但是通过闭包会仍然保存在内存中

  2. 延长作用域链 —— 创造闭包自然增加作用域链

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function f1() {
    var num = 1;
    function f2() {
    num++;
    console.log(num)
    }
    return f2;
    }
    f1(); // 1
    f1(); // 2
    f1(); // 3

    上面的例子中,f1 外部函数调用完毕后,数据 num 并没有消失,而是仍旧存在内存中,可以循环使用,这里也就可以在函数外部不断操作它;通过创造闭包增加了内部函数,也就增加了一层作用域链。

1
2
3
4
5
6
7
8
function f3() {
var num = 10;
var obj = {
age: num
};
console.log(obj.age)
}
f3(); // 10

除了上面构造内部函数的方式创造闭包,还有一种对象模式闭包。这个例子就是通过在内部创建一个对象,然后再对象中调用外部函数中的数据来创造闭包。两者只是形式不一样,创建方式和作用都相同。

原型

作用

共享数据,节省内存空间。

将需要共享的属性和方法直接定义在 prototype 对象中,他们会存在同一个内存地址中,因此还会提高运行效率。

prototype 和 _ proto _

_proto_ 是实例对象中的一个属性,也可以被看作一个对象,它是原型,不是标准的属性, 供浏览器使用。

prototype 是构造函数中的一个属性,也可以被看作一个对象,它是原型,也是标准的属性,供程序员使用,它默认有一个 constructor 属性,指向 prototype 对象所在函数。

原型链

定义

是指实例对象与原型对象之间的关系,通过原型(_proto_)来联系。

实例对象调用属性或方法的搜索原则

当一个实例对象需要某个属性或方法时:

  1. 先从实例对象本身搜索
  2. 若找到,则返回该属性或方法的值
  3. 如果没有找到,则顺着原型链继续搜索该实例对象指向的原型对象
  4. 若找到,则返回该属性值
  5. 如果一直到原型链末端都没有找到,则返回 undefined

继承

通过构造函数的方式

1
2
3
4
5
6
7
8
function Person(name,age) {
this.name = name;
this.age = age;
}
function Student(name,age,sex) {
Person.call(this,name,age); // 通过 构造函数.call() 的方法解决属性继承问题,并且继承的属性值不重复,不过不能继承方法
this.sex = sex;
}

通过改变原型指向方法

1
2
3
4
5
6
7
8
9
10
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
Student.prototype = new Person() // 通过为实例对象的原型重新指向一个新的构造函数对象,来改变原型的指向,这里不传参数,就可以只继承方法
var s1 = Student('张三', 18);
s1.sayName() // hello 张三

this

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

调用方式 非严格模式 备注
普通函数调用 window 严格模式下是 undefined
构造函数调用 实例对象 原型方法中 this 也是实例对象
对象方法调用 该方法所属对象 紧挨着的对象
事件绑定方法 绑定事件对象
定时器函数 window

这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

本文标题:关于 js

文章作者:J、Making

发布时间:2018年05月15日 - 21:05

原始链接:https://jmaking.top/js/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

留出空白
-------------本文结束 感谢您的阅读-------------