初学者前端--基础知识要点(一)

39 阅读8分钟

壹、构造函数

实例成员: 实例成员就是在构造函数内部,通过this添加的成员。实例成员只能通过实例化的对象来访问。

静态成员: 在构造函数本身上添加的成员,只能通过构造函数来访问

new一个新对象的过程,发生了什么?

(1) 创建一个空对象 son {} (2) 为 son 准备原型链连接 son.proto = Father.prototype (3) 重新绑定this,使构造函数的this指向新对象 Father.call(this) (4) 为新对象属性赋值 son.name (5) 返回this return this,此时的新对象就拥有了构造函数的方法和属性了

公共属性定义到构造函数里面,公共方法我们放到原型对象身上。

类和构造函数的区别

(1) 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

(2) 类的所有实例共享一个原型对象。

(3) 类的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。

构造函数特点:

1.构造函数有原型对象prototype。

2.构造函数原型对象prototype里面有constructor,指向构造函数本身。

3.构造函数可以通过原型对象添加方法。

4.构造函数创建的实例对象有__proto__原型,指向构造函数的原型对象。

类:

1.class本质还是function

2.类的所有方法都定义在类的prototype属性上

3.类创建的实例,里面也有__proto__指向类的prototype原型对象

4.新的class写法,只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。

5.ES6的类其实就是语法糖。

ps:什么是语法糖?加糖后的代码功能与加糖前保持一致,糖在不改变其所在位置的语法结构的前提下,实现了运行时的等价。

语法糖没有改变语言功能,但增加了程序员的可读性。

贰、原型和原型链

(a) 原型

(a).1 什么是原型?

Father.prototype 就是原型,它是一个对象,我们也称它为原型对象。

(a).2 原型的作用是什么?

原型的作用,就是共享方法。 我们通过 Father.prototype.method 可以共享方法,不会反应开辟空间存储方法。

(a).3 原型中this的指向是什么?

原型中this的指向是实例。

(b) 原型链

(b).1 什么是原型链?

原型与原型层层相链接的过程即为原型链。

(b).2 原型链应用

对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在 每个对象都有__proto__原型的存在

function Peoples(name,age) { 
    this.name = name; 
    this.age = age; 
} 
Peoples.prototype.doiit = function(){ 
    console.log('小帅哥过来玩啊',this.name); 
}; 
let obj = new Peoples('张三',18);
console.log(obj.__proto__ === Peoples.prototype);//true

叁、作用域

作用域分为全局作用域、函数作用域以及es6新增的块级作用域。

1.全局作用域

顾名思义:能够在代码中任何地方都能访问到的对象拥有全局作用域,一般有三种情况的全局作用域:

1.1 window对象的属性拥有全局作用域

1.2 所有末定义直接赋值的变量自动声明为拥有全局作用域

1.3 在最外层定义的函数 和变量 都拥有全局作用域

全局作用域有个弊端:如果我们写了很多行 JS 代码,变量定义都没有用函数包括,那么它们就全部都在全局作用域中。这样就会 污染全局命名空间, 容易引起命名冲突。这就是为何 jQuery、Zepto 等库的源码,所有的代码都会放在(function(){....})()中。因为放在里面的所有变量,都不会被外泄和暴露,不会污染到外面,不会对其他的库或者 JS 脚本造成影响。这是函数作用域的一个体现。

2.函数作用域

函数作用域,是指声明在函数内部的变量,和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部。

作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。

块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域。在块语句中定义的变量将保留在它们已经存在的作用域中。

3.块级作用域

块级作用域可通过新增命令let和const声明,所声明的变量在指定块的作用域外无法被访问。块级作用域在如下情况被创建:

  1. 在一个函数内部
  2. 在一个代码块(由一对花括号包裹)内部

let 声明的语法与 var 的语法一致。你基本上可以用 let 来代替 var 进行变量声明,但会将变量的作用域限制在当前代码块中。块级作用域有以下几个特点:

  • 声明变量不会提升到代码块顶部

let/const 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let/const 声明放置到顶部,以便让变量在整个代码块内部可用。

function getValue(boor) { 
    if (boor) { 
        let a = "blue"; 
        return a; 
    } else { 
        // a 在此处不可用 
        return null; 
    } 
    // a 在此处不可用 
}
  • 禁止重复声明

如果一个标识符已经在代码块内部被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。例如:

var a = 30; 
let a = 40; // Uncaught SyntaxError: Identifier 'a' has already been declared

在本例中, a 变量被声明了两次:一次使用 var ,另一次使用 let 。因为 let 不能在同一作用域内重复声明一个已有标识符,此处的 let 声明就会抛出错误。但如果在嵌套的作用域内使用 let 声明一个同名的新变量,则不会抛出错误。

var num = 30; 
// 不会抛出错误 
if (true) { 
    let num = 40; 
    // 其他代码 
}
  • 循环中的绑定块作用域的妙用

我们可能最希望实现for循环的块级作用域了,因为可以把声明的计数器变量限制在循环内,例如:

for (let i = 0; i < 10; i++) {
    // ... 
} 
console.log(i); // ReferenceError: i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。

var a = [];
for (var i = 0; i < 10; i++) { 
    a[i] = function () { 
        console.log(i); 
    }; 
} 
a[6](); // 10

上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = []; 
for (let i = 0; i < 10; i++) { 
    a[i] = function () { 
        console.log(i); 
    };
} 
a[6](); // 6

上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) { 
    let i = 'abc'; 
    console.log(i); 
} 
// abc 
// abc 
// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

4.作用域与执行上下文

许多开发人员经常混淆作用域和执行上下文的概念,误认为它们是相同的概念,但事实并非如此。

我们知道JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:

解释阶段:

  • 词法分析
  • 语法分析
  • 作用域规则确定

执行阶段:

  • 创建执行上下文
  • 执行函数代码
  • 垃圾回收

JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。

作用域和执行上下文之间最大的区别是:

执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。