专栏名称: Fundebug
Fundebug为JavaScript、微信小程序及Node.js开发团队提供专业的线上代码bug监控和智能分析服务。
目录
相关文章推荐
前端早读课  ·  【早阅】始终将你的估算值乘以π ·  7 小时前  
前端大全  ·  从 DeepSeek 看25年前端的一个小趋势 ·  昨天  
前端大全  ·  10年了,开发人员仍然不明白 ... ·  昨天  
前端早读课  ·  【第3454期】如何用语音学习编程的 ·  2 天前  
前端大全  ·  前端行情变了,差别真的挺大。。。 ·  3 天前  
51好读  ›  专栏  ›  Fundebug

快速掌握JavaScript面试基础知识(三)

Fundebug  · 公众号  · 前端  · 2018-01-29 09:18

正文

译者按: 总结了大量JavaScript基本知识点,很有用!

  • 原文: The Definitive JavaScript Handbook for your next developer interview

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。



根据StackOverflow调查, 自2014年一来,JavaScript是最流行的编程语言。当然,这也在情理之中,毕竟1/3的开发工作都需要一些JavaScript知识。因此,如果你希望在成为一个开发者,你应该学会这门语言。

这篇博客的主要目的是将所有面试中常见的概念总结,方便你快速去了解。(鉴于本文内容过长,方便阅读,将分为三篇博客来翻译, 此为第三部分。第一部分请点击快速掌握JavaScript面试基础知识(一))

new关键字

如果使用 new 关键字来调用函数式很特别的形式。我们把那些用 new 调用的函数叫做构造函数(constructor function)。

使用了 new 的函数到底做了什么事情呢?

  • 创建一个新的对象

  • 将对象的prototype设置为狗仔函数的prototype

  • 执行构造函数, this 执行新构造的对象

  • 返回该对象。如果构造函数返回对象,那么返回该构造对象。

// 为了更好地理解底层,我们来定义new关键字
function myNew(constructor, ...arguments) {
 var obj = {}
 Object.setPrototypeOf(obj, constructor.prototype);
 return constructor.apply(obj, arguments) || obj
}

使用 new 和不使用的区别在哪里呢?

function Bird() {
 this.wings = 2;
}
/* 普通的函数调用 */
let fakeBird = Bird();
console.log(fakeBird);    // undefined
/* 使用new调用 */
let realBird= new Bird();
console.log(realBird)     // { wings: 2 }

为了便于对比理解,译者额外增加了测试了一种情况:

function MBird




    
(){
 this.wings =2;
 return "hello";
}

let realMBrid = new MBird();
console.log(realMBird) // { wings: 2 }


你会发现,这一句 return "hello" 并没有生效!

原型和继承

原型(Prototype)是JavaScript中最容易搞混的概念,其中一个原因是 prototype 可以用在两个不同的情形下。

  • 原型关系
    每一个对象都有一个 prototype 对象,里面包含了所有它的原型的属性。
    .__proto__ 是一个不正规的机制(ES6中提供),用来获取一个对象的prototype。你可以理解为它指向对象的 parent
    所有普通的对象都继承 .constructor 属性,它指向该对象的构造函数。当一个对象通过构造函数实现的时候, __proto__ 属性指向构造函数的构造函数的 .prototype Object.getPrototypeOf() 是ES5的标准函数,用来获取一个对象的原型。

  • 原型属性
    每一个函数都有一个 .prototype 属性,它包含了所有可以被继承的属性。该对象默认包含了指向原构造函数的 .constructor 属性。每一个使用构造函数创建的对象都有一个构造函数属性。

接下来通过例子来帮助理解:

function Dog(breed, name){
 this.breed = breed,
 this.name = name
}
Dog.prototype.describe = function() {
 console.log(`${this.name} is a ${this.breed}`)
}
const rusty = new Dog('Beagle', 'Rusty');

/* .prototype 属性包含了构造函数以及构造函数中在prototype上定义的属性。*/
console .log(Dog.prototype)  // { describe: ƒ , constructor: ƒ }

/* 使用Dog构造函数构造的对象 */
console.log(rusty)   //  { breed: "Beagle", name: "Rusty" }
/* 从构造函数的原型中继承下来的属性或函数 */
console.log(rusty.describe())   // "Rusty is a Beagle"
/* .__proto__ 属性指向构造函数的.prototype属性 */
console.log(rusty.__proto__)    // { describe: ƒ , constructor: ƒ }
/* .constructor 属性指向构造函数 */
console.log(rusty.constructor)  // ƒ Dog(breed, name) { ... }

JavaScript的使用可以说相当灵活,为了避免出bug了不知道,不妨接入Fundebug线上实时监控

原型链

原型链是指对象之间通过prototype链接起来,形成一个有向的链条。当访问一个对象的某个属性的时候,JavaScript引擎会首先查看该对象是否包含该属性。如果没有,就去查找对象的prototype中是否包含。以此类推,直到找到该属性或则找到最后一个对象。最后一个对象的prototype默认为null。

拥有 vs 继承

一个对象有两种属性,分别是它自身定义的和继承的。

function Car() { }
Car.prototype.wheels = 4;
Car.prototype.airbags = 1;

var myCar = new Car();
myCar.color = 'black';

/*  原型链中的属性也可以通过in来查看:  */
console.log('airbags' in myCar)  // true
console.log(myCar.wheels)        // 4
console.log(myCar.year)          // undefined

/*  通过hasOwnProperty来查看是否拥有该属性:  */
console.log(myCar.hasOwnProperty('airbags'))  // false — Inherited
console.log(myCar.hasOwnProperty('color'))    // true

Object.create(obj) 创建一个新的对象,prototype指向 obj

var




    
 dog = { legs: 4 };
var myDog = Object.create(dog);

console.log(myDog.hasOwnProperty('legs'))  // false
console.log(myDog.legs)                    // 4
console.log(myDog.__proto__ === dog)       // true

继承是引用传值

继承属性都是通过引用的形式。我们通过例子来形象理解:

var objProt = { text: 'original' };
var objAttachedToProt = Object.create(objProt);
console.log(objAttachedToProt.text)   // original

// 我们更改objProt的text属性,objAttachedToProt的text属性同样更改了
objProt.text = 'prototype property changed';
console.log(objAttachedToProt.text)   // prototype property changed

// 但是如果我们讲一个新的对象赋值给objProt,那么objAttachedToProt的text属性不受影响
objProt = { text: 'replacing property' };
console.log(objAttachedToProt.text)   // prototype property changed


经典继承 vs 原型继承

Eric Elliott的文章有非常详细的介绍:Master the JavaScript Interview: What’s the Difference Between Class & Prototypal Inheritance?
作者认为原型继承是优于经典的继承的,并提供了一个视频介绍:https://www.youtube.com/watch?v=wfMtDGfHWpA&feature=youtu.be

异步JavaScript

JavaScript是一个单线程程序语言,也就是说JavaScript引擎一次只能执行某一段代码。它导致的问题就是:如果有一段代码需要耗费很长的时间执行,其它的操作就被卡住了。JavaScript使用Call Stack来记录函数的调用。一个Call Stack可以看成是一摞书。最后一本书放在最上面,也最先被移走。最先放的书在最底层,最后被移走。

为了避免复杂代码占用CPU太长时间,一个解法就是定义异步回调函数。我们自己来定义一个异步函数看看:

function greetingAsync(name, callback){
 let greeting = "hello, " + name ;
 callback(greeting);
}

greetingAsync("fundebug", console.log);
console.log("start greeting");

我们在 greetingAsync 中构造了 greeting 语句,然后通过 callback ,让用户自己去定义greeting的具体方式。为方便起见,我们时候直接使用 console.log
上面代码执行首先会打印 start greeting ,然后才是 hello, fundebug 。也就是说, greetingAsync 的回调函数后执行。在网站开发中,和服务器交互的时候需要不断地发送各种请求,而一个页面可能有几十个请求。如果我们一个一个按照顺序来请求并等待结果,串行的执行会使得网页加载很慢。通过异步的方式,我们可以先发请求,然后在回调中处理请求结果,高效低并发处理。







请到「今天看啥」查看全文