坑爹面试题之高程三(3)

314 阅读6分钟

本文会用各种刁钻的角度来问问题,不考虑实际价值与这样写对不对,只作为考察(我自己)是否吃透(或者是还记得)高程三,防止各种坑爹面试题。(小部分补充不属于高程三)

问题

1. 为什么函数没有重载?

2. 函数声明和函数表达式有什么区别?

3. 请问这段函数输出什么?

function outer(a,b,c) {
  inner()
}
function inner() {
  console.log(arguments.callee.caller.length)
  console.log(arguments.callee.name[3])
}
outer()

4. 请问各种情况下this的指向。

5. 请问以下代码输出什么?

var color = "red"
var o = { color: "blue" }
function sayColor() {
  console.log(this.color)
}
var objectSayColor = sayColor.bind(o)
sayColor()

6. 以下声明的3个变量有什么区别?

var obj = new Object('hello world')
var num1 = Number('25')
var num2 = new Number('25')

7. 请问以下代码的输出

console.log(99.9.toFixed(1))
console.log(99.9.toExponential(1))
console.log(99.9.toPrecision(1))

8. 请问以下代码的输出

var stringValue = "hello world"
console.log(stringValue.slice(-3))
console.log(stringValue.substring(-3))
console.log(stringValue.substr(-3))
console.log(stringValue.slice(3, -4))
console.log(stringValue.substring(3, -4))
console.log(stringValue.substr(3, -4))

9. 请问以下代码的输出

var text = 'this has been a short summer'
var pattern = /(.)hort/g
if (pattern.test(text)) {
  console.log(RegExp.$_)
  console.log(RegExp['$`'])
  console.log(RegExp["$'"])
  console.log(RegExp['$&'])
  console.log(RegExp['$+'])
  console.log(RegExp['$*'])
}

10. 请用replace函数一次性将文本text中的<>"&这4个字符替分别换成HTML特殊字符编码

11. 请问以下代码输出什么?

var colorText = "red, blue, green, yellow"
console.log(colorText.split(/[^\,]+/, 3))

12. 将一段中文,进行转换后与百度的搜索链接进行拼接,并打开网页。(即用搜索引擎的链接拼接上转码后的中文。)

13. 请问以下代码输出什么?

var msg = "hello world"
eval('console.log(msg)')
sayHi()
eval('function sayHi() { console.log("hi") }')

14. 为什么在ES3中undefined、NaN和Infinity可以被赋值,而null不可以?ES5中undefined、NaN和Infinity可以被赋值吗?

15. 如何在对象中定义一个只读属性?

16. 请问以下代码输出啥?

var aaa = {}
Object.defineProperty(aaa, 'o', {})
console.log(aaa.o)
aaa.o = 100
console.log(aaa.o)

17. var obj={a: 1} Object.freeze(obj)修改了属性a的什么特性?

—————以上是高程三前20%的内容—————

答案

1. 因为函数是对象,函数名是指针,声明同名函数只会导致后者覆盖前者。下边的代码可以帮助理解“函数是对象,函数名是指针”的概念,前边可以有N个参数,最后一个是函数体。// 不推荐这样写,会导致二次解析代码,影响性能。(高程三110页)

var sum = new Function("num1", "num2", "return num1 + num2");

2. 在预编译时,解析器会通过一个名为函数声明提升的过程,这使得函数声明可以在任何地方使用,而函数表达式只能在声明之后才能使用。

3. 3和'e'。原理: arguments的callee属性是一个指针,指向拥有这个arguments对象的函数。(高程三113页)这样写可以解耦,使得该函数无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。// 当函数在严格模式下运行时,访问arguments.callee会导致错误。(高程三115页),不过可以通过命名函数表达式来达成相同的结果(严格模式或非严格模式都行得通),代码如下:

var factorial = (function f(num){
    if (num <= 1){
        return 1;
    } else {
        return num * f(num - 1);    
    }
});
  • 所以arguments.callee在以上代码中表示inner函数。
  • caller这个属性中保存着调用当前函数的函数的引用(高程三115页)严格模式下赋值会报错(高程三115页)
  • 所以arguments.callee.caller,即inner.caller指向outer函数。
  • 每个函数都包含length属性,表示函数希望接收的命名参数的个数。
  • 每个函数都有name属性,保存着函数名,inner.name就是inner。
  • ECMAScript5还定义了另一个访问个别字符的方法。IE8+的浏览器中,可以使用方括号加数字索引来访问字符串中的特定字符。(高程三123页)

4. this引用的是函数执行的环境对象(高程三114页),所以在不同情况时有不同指向。如下:

  1. apply(obj,paramsArray)函数的this指向obj

  2. bind(this)这个this永远指向自身

  3. call(obj, param1, param2...)函数的this指向obj//和apply只有参数接收方式不同

  4. 全局作用域定义的函数this指向window

  5. 对象里的this指向对象  //除非异步调用setTimeout(myObj.fn,1000)解决办法:用函数包裹 setTimeout(() => { myObj.fn() }, 1000)(2020/06/21更新)

  6. with(obj){this}这里延长了作用域链,this指向obj

  7. 构造函数里的this指向新生成的空对象{},然后各种赋值,最后返回新生成的obj。

  8. 箭头函数的this永远指向上一级

  9. 异步函数中的this指向window

  10. 事件触发时,this指向绑定元素

在没有给函数明确指定this值的情况下,this值都等于Global对象。(浏览器将Global对象作为window对象的一部分加以实现)(高程三133页)

例题// 2020/08/02更新

var name1 = 'The window';
var obj1 = {
  name1: 'My Object',
  getNameFunc: function() {
    return function() {
      return this.name1;
    }
  }
}
console.log(obj1.getNameFunc()());// The window(实际调用者为window)
var name2 = 'The window';
var obj2 = {
  name2: 'My Object',
  getNameFunc: function() {
    var that = this;
    return function() {
      return that.name2;
    }
  }
}
console.log(obj2.getNameFunc()());// My Object(这里用了闭包)

5. red。objectSayColor()才输出blue。原理: bind()方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。(高程三118页)

6. obj是String类型的实例,num1是数字25,num2是Number的实例。Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。把字符串传给Object构造函数,就会创建String的实例;而传入数值参数会得到Number的实例,传入布尔值参数就会得到Boolean的实例。(高程三119页)

Number、String 和 Boolean,三个构造器是两用的,当跟 new 搭配时,它们产生对象,当直接调用时,它们表示强制类型转换。(重学前端 模块一第一讲)

7. 99.9、1.0e+2和1e+2。toFixed()方法会按照指定的小数位返回数值的字符串表示。toExponential()返回以指数表示法表示的数值的字符串形式。这两个方法接收的参数都是用于指定输出结果中的小数位数。toPrecision()方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看哪种格式最合适。这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。(高程三121页)

8. 'rld', 'hello world', 'rld', 'lo w', 'hel', ''。三者关系如下。(高程三123页)

9. "this has been a short summer"、"this has been a "、" summer"、"short"、"s"、undefined。原理: 略(高程三109页)

10. 代码如下(高程三128页)

text.replace(/[<>"&]/g, function(match, index, input) {
  switch (match) {
    case "<":
      return "&lt;"
    case ">":
      return "&gt;"
    case "&":
      return "&amp;"
    case "\"":
      return "&quot;"
  }
}

11. [ "", ",", "," ]。不加第二个参数的输出是[ "", ",", ",", ",", "" ],加了之后返回的数组长度就只有3了。(高程三128页)

12. 代码如下(高程三131页)

var text = '叶修'
var parseStr = encodeURIComponent(text)
window.location.href=`https://www.baidu.com/baidu?wd=${parseStr}&tn=monline_4_dg&ie=utf-8`

13. hello world 并报错。原理: 通过eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。(高程三132页)

在eval()中创建的任何变量或函数都不会被提升,因为再解析代码的时候,它们被包含在一个字符串中;它们只在eval()执行的时候创建。

严格模式下,在外部访问不到eval()中创建的任何变量或函数,为eval()赋值也会导致错误。(高程三133页)

14. 因为它们都是值而非关键字,所以在ES3中,可以被赋值,从而导致一系列bug,而null一直是一个关键字,ES5则全都不能赋值。原理: ES5明确禁止给undefined、NaN和Infinity赋值,这样做即使在非严格模式下也会导致错误。(高程三133页)

Undefined 类型表示未定义,它的类型只有一个值,就是 undefined。任何变量在赋值前是 Undefined 类型、值为 undefined,一般我们可以用全局变量 undefined(就是名为 undefined 的这个变量)来表达这个值,或者 void 运算来把任意一个表达式变成 undefined 值。但是呢,因为 JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,这是 JavaScript 语言公认的设计失误之一,所以,我们为了避免无意中被篡改,我建议使用 void 0 来获取 undefined 值。(重学前端模块一第一讲)

NaN,占用了 9007199254740990这个数值。(重学前端模块一第一讲)Infinity也是个值。

可否赋值这个问题可以在较早的浏览器中可做验证,如ie7,vue需要从es5的版本改成es3的版本,或者静态页要实现的话改dtd模式4.01版本。(林小帅大佬)

15. 代码如下。原理: 要修改属性默认的特性,必须使用ES5的Object.defineProperty()方法。(高程三139页)

var person = {}
Object.defineProperty(person, "name", {
  writable: false,
  value: "Nicholas",
  enumerable: true,
  configurable: true
})
// console.log(person.name)
// person.name = "Greg"
// console.log(person.name)

16. undefined undefined。在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable、enumerable和writable特性的默认值是false。(高程三140页)//即不能删改该属性的任何一个特性。

17. writable和configurable,都变成了false。原理: ECMAScript中有两种属性:数据属性和访问器属性。直接在对象上定义的数据属性,它们的[[Configurable]]、[[Enumerable]]和[[Writable]]特性都被设置为true,而[[Value]]特性被设置为指定的值。(高程三139页)

使用ES5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。(高程三143页)而我们通过这个方法可以知道writable和configurable,都变成了false,代码如下:

var asd = {aaa: 1}
var descriptor = Object.getOwnPropertyDescriptor(asd, 'aaa')
console.log(descriptor) //Object { value: 1, writable: true, enumerable: true, configurable: true }

Object.freeze(asd)
var descriptor = Object.getOwnPropertyDescriptor(asd, 'aaa')
console.log(descriptor) //Object { value: 1, writable: false, enumerable: true, configurable: false }