Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面试常见问题 #33

Open
Manjushaka opened this issue Jan 30, 2019 · 8 comments
Open

面试常见问题 #33

Manjushaka opened this issue Jan 30, 2019 · 8 comments

Comments

@Manjushaka
Copy link
Owner

Manjushaka commented Jan 30, 2019

  1. Promise
    const promise = new Promise(function (resolve, reject) {
      setTimeout(function () { reject('reject 1') }, 0);
      resolve('ok')
      setTimeout(function () { reject('reject 2') }, 0);
    });
    promise.then(function (value) { console.log(value) })
    .catch((error) => {
      console.log('error: ', error);
    });

输出结果:

ok

先输出ok可见,立即resolved的promise,是在本轮同步代码执行结束就执行的,而setTimeout要等到下一轮事件循环开始。

@Manjushaka
Copy link
Owner Author

Manjushaka commented Apr 9, 2019

new操作符的操作:
1、创建一个空对象,作为将要返回的对象实例。
2、继承函数的原型,将这个空对象的原型,指向构造函数的prototype属性。
3、将这个空对象赋值给函数内部的this关键字,并执行该函数。
4、新创建的对象由 this 所引用,并且最后隐式的返回 this 。// 如果func.call(target)返回的res是个对象或者function 就返回它

  function new() {
      let args = [].slice.call(arguments);
      const construct = args.shift();
      let target = {}; // let target = Object.create(construct.prototype);
      target.__proto__ = construct.prototype;
      const res = construct.apply(target, args);
      if ((typeof res === 'object' && res !== null) || typeof res === 'function') {
          return res;
      }

      return target;
  }

@Manjushaka
Copy link
Owner Author

Manjushaka commented Apr 23, 2019

客户端存储:

1. cookies
服务端发送给客户端,客户端存储,随后在请求中发回给服务端。可用于管理用户会话,追踪用户信息等。客户端也可以用cookies存储数据,通常是一些通用数据,如用户首选项设置。
CRUD:

// Create
document.cookie = "user_name=Ire Aderinokun";  
document.cookie = "user_age=25;max-age=31536000;secure";
// Read (All)
console.log( document.cookie );
// Update
document.cookie = "user_age=24;max-age=31536000;secure"; 
// Delete 就是把cookie设置为过期即是删除。
document.cookie = "user_name=Ire Aderinokun;expires=Thu, 01 Jan 1970 00:00:01 GMT";  

expires的日期格式类似于new Date().toUTCString(), 返回格式"Tue, 23 Apr 2019 06:06:59 GMT"。(new Date().toGMTString()已被废弃,不要使用)
优点:能用于和服务端通信;当 cookie 快要自动过期时,我们可以重新设置而不是删除
缺点:增加了文档传输的负载;只能存储少量的数据;只能存储字符串;潜在的 安全问题;自从有 Web Storage API (Local and Session Storage),cookies 就不再被推荐用于存储数据了
2.

@Manjushaka
Copy link
Owner Author

Manjushaka commented May 29, 2019

跨域问题:

  1. 九种跨域方式实现原理(完整版)
  2. 菜鸟教程简单的JSONP例子
  3. JSONP的原理文章1
  4. iframe跨域+
    iframe在得到b.html的内容后,必须将src变为a.html的同源域,因为同源策略是会阻止非同源的frame访问name属性值,最后a.html通过iframe.contentWindow.name,获取b.html里window.name的值,从而实现跨域获得数据。

@Manjushaka
Copy link
Owner Author

Manjushaka commented Jun 5, 2019

作用域与作用域链:

  1. 深入理解JavaScript作用域和作用域链
    文章注意点:作用域Scope
    1)作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6的到来,为我们提供了‘块级作用域’,可通过新增命令let和const来体现。
    2)for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
 let i = 'abc';
 console.log(i);
}
// abc
// abc
// abc

3)。当前作用域没有定义的变量,这成为 自由变量 。自由变量的值如何得到 —— 向父级作用域寻找(注意:这种说法并不严谨,下文会重点解释)。
4)在fn函数中,取自由变量x的值时,要到哪个作用域中取?——要到创建fn函数的那个作用域中取,无论fn函数将在哪里调用。所以,不要在用以上说法了。相比而言,用这句话描述会更加贴切:要到创建这个函数的那个域”。作用域中取值,这里强调的是“创建”,而不是“调用”,切记切记——其实这就是所谓的"静态作用域"

var a = 10
function fn() {
  var b = 20
  function bar() {
    console.log(a + b) //30
  }
  return bar
}
var x = fn(),
  b = 200
x() //bar()

fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了,所以最后的结果是30

var x = 10
function fn() {
  console.log(x)
}
function show(f) {
  var x = 20
  (function() {
    f() //10,而不是20
  })()
}
show(fn)

5)我们知道JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段:词法分析 语法分析 作用域规则确定
执行阶段:创建执行上下文 执行函数代码 垃圾回收
JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别是:
执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。

@Manjushaka
Copy link
Owner Author

Manjushaka commented Jun 5, 2019

不错的文章:
(1)深入学习js之——原型和原型链(知乎)
(2)博客园中几篇很好的文章:按顺序先后阅读:
JavaScript的执行上下文 VO/AO
理解JavaScript的作用域链 Scope Chain
JavaScript中的this this
彻底理解JavaScript原型

@Manjushaka
Copy link
Owner Author

Manjushaka commented Jun 6, 2019

开发者可能最希望实现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不在同一个作用域,有各自单独的作用域。

@Manjushaka
Copy link
Owner Author

Manjushaka commented Jun 14, 2019

http与https:

  1. 数字签名
  2. 输入理解https工作原理
  3. 图解SSL/TLS协议

1)server向ca提供基本信息、公钥等,ca用ca私钥对信息和公钥进行加密形成数字证书(防止server被伪造)。
2)client向server发出https请求和随机数c,server返回数字证书和随机数s。
3)client收到数字证书,用ca公钥对数字证书进行解密,得到server的公钥。server公钥加密client生成的随机数premaster secret,发送给server。server收到加密的随机数premaster secret,用server私钥解密随机数,然后和随机数c、随机数s一起生成会话密钥。此时client也拥有三个随机数生成相同的会话密钥。双方用会话密钥进行加密通信(防止内容被窃听进行加密)。

用会话密钥进行加密通信是对称加密的,而获得会话密钥的过程是不对称加密的。
防止数据被篡改:数字签名是server对通信内容进行hash,得到信息摘要。然后用server私钥对摘要进行加密得到数字签名。server把数据连同数字签名一起发送给client。client用server公钥对数字签名解密得到信息摘要,然后用相同的hash对数据进行计算得到信息摘要,比对两个信息摘要是否一致。

@Manjushaka
Copy link
Owner Author

Manjushaka commented Jun 25, 2019

let const var
let/const 也是存在变量提升的,只不过,它们是块级作用域,存在一个暂时性死区(temporal dead zone, tdz),在let/const变量声明之前,是无法访问的。
证明变量提升了:

let name = 'lili'
{
  console.log(name) // Uncaught ReferenceError: name is not defined
  let name = 'tom'
}

let变量如果不存在变量提升,console.log(name)就会输出lili,结果却抛出了ReferenceError,那么这很好的说明了,let也存在变量提升,但是它存在一个“暂时死区”,在变量未初始化或赋值前不允许访问。

变量的赋值可以分为三个阶段:
创建变量,在内存中开辟空间
初始化变量,将变量初始化为undefined
真正赋值

关于let、var和function:
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant