js中的包装对象
在JavaScript中有三种包装对象,他们对应的构造函数分别是String,Number,Boolean
当我们使用原始类型调用toString方法的时候,原始类型会先通过其包装对象对应的构造函数转换成对象,然后用这个对象调用方法,调用方法之后,原始类型仍是原始类型,新创建的包装对象会被销毁
const b = new Number(22); b.toFixed(2); // b 是一个对象,对象上有toFixed方法 const a = 22; a.toFixed(2); // b 是一个数字,数字上怎么会有toFixed方法? (22).toFixed(2); // 调用的时候隐式的包装一下 // let temp = new Number(22) // value = temp.toFixed(2) // 删除 temp // return value
this 指向
JS(ES5)里面有三种函数调用形式:
func(p1, p2) obj.child.method(p1, p2) func.call(context, p1, p2)
只需要知道第三种调用形式,才是正常调用形式就很好理解this的指向了
func(p1, p2) 等价于 func.call(undefined, p1, p2) obj.child.method(p1, p2) 等价于 obj.child.method.call(obj.child, p1, p2)
至此我们的函数调用只有一种形式
func.call(context, p1, p2)
这样,this 就好解释了
function func(){ console.log(this) } func() // 等价于 function func(){ console.log(this) } func.call(undefined) // 可以简写为 func.call()
var obj = { foo: function(){ console.log(this) } } obj.foo() // 等价于 obj.foo.call(obj)
this的变态面试题
let length = 10 function fn() {console.log(this.length)} let obj = { length: 5, method(fn) { fn() // fn.bind(undefined) arguments[0]() // arguments[0].bind(arguments) => fn.bind(arguments) } } obj.method(fn, 1) // 会打印 3 和 2
proto 和 prototype
要了解__proto__ 和 prototype需要了解原型和构造函数
// 构造函数 function Person(name) { this.name = name; } Person.prototype.run = function () { console.log('run'); } // 或者这样 Person.prototype = { constructor: Person, // 在构造函数中初始化独有属性 run: function () { // 共有属性 console.log('run'); } } const p = new Person('x')
new做了什么
- 不用创建临时对象,因为 new 会帮你做
- 不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype)
- 不用 return 临时对象,因为 new 会帮你做;
- 不要给原型想名字了,因为 new 指定名字为 prototype。
// 1 2 3 为new做的事情 const p = function('x') { this = {} // 1. 初始化this this.name = 'x'; // 初始化独有属性 this.__proto__ = Person.prototype; // 2. 构造函数添加__proto__并指向prototype(共有属性) return this; // 3. 返回当前对象 }
所以就可以推断出这样:
a = {} // new Object() // 1. new 之后就有__proto__ // 2. __proto__等于普通对象的prototype a.__proto__ === window.Object.prototype // true // 原型(共有属性)找到根上就是null了 window.Object.prototype.__proto__ // null
再看__proto__和prototype

// prototype 指向一块内存,这个内存里面有共用属性 // __proto__ 指向同一块内存 // prototype 和 __proto__ 的不同点在于prototype 是构造函数的属性,而 __proto__ 是对象的属性 // 难点在于……构造函数也是对象! // 如果没有 prototype,那么共用属性就没有立足之地 // 如果没有 __proto__,那么一个对象就不知道自己的共用属性有哪些。 var a = {} // new Object() a.toString === window.Object.prototype.toString // true a.__proto__.toString === window.Object.prototype.toString // true var b = [] // new Array() b.split === window.Array.prototype.split // true b.__proto__.split === window.Array.prototype.split // true window.Object.prototype.__proto__ // null
简单手写
trim
const trim = (str) =>{ return str.replace(/^\s+|\s+$/g, '') } const trim2 = (str) => { while (str[0] === ' ') { str = str.slice(1) } while (str[str.length - 1] === ' ') { str = str.slice(0, -1) } return str }
bind
const bind = function(asThis, ...args) { return (...args2) => { this.call(asThis, ...args, ...args2); } }
关于闭包
经典面试题:
// 打印6个6 for (var i = 0; i < 6; i++) { setTimeout(() => { console.log(i) }, 0) } // 怎么解决两种方案 一种用匿名函数 另一种使用let // 原因 用var时所有闭包公用一个i 用let每个闭包分配一个i for (var i = 0; i < 6; i++) { !function (i) { setTimeout(() => { console.log(i) }, 0) }(i) } for (let i = 0; i < 6; i++) { setTimeout(() => { console.log(i) }, 0) }
闭包的特点:
- 能让一个函数维持住一个变量
- 但并不能维持住这个变量的值
- 尤其时变量的值会变化的时候
对象是穷人的闭包
- 对象可以维持住一个变量
- 如果一门语言不支持闭包,可以用对象代理
闭包是穷人的对象
如果一门语言不支持对象,可以用闭包代理
箭头函数
箭头函数对this与其他变量一视同仁,不会特殊对待。
关于var
var生命的变量会自动提升,全局下会自动挂载到window上。
JS内置的高阶函数
Function.prototype.bind Function.prototype.apply Function.prototype.call Function.prototype.sort Function.prototype.map Function.prototype.filter Function.prototype.reduce
- bind.call
var bind = Function.prototype.bind; var f1 = function () { console.log(this); console.log(arguments); console.log("---------"); }; var newF1 = f1.bind({ a: 1 }, 1, 2, 3); // console.log(newF1()); // 定理: 以下两个等价 // obj.method(a,b,c) // obj.method.call(obj, a,b,c) // 设 obj = f1 // 设 method = bind // 代入 // f1.bind(a,b,c) // f1.bind.call(f1, a,b,c) // 代入参数 // f1.bind({ a: 1 }, 1, 2, 3) // f1.bind.call(f1, { a: 1 }, 1, 2, 3) // f1.bind === Function.prototype.bind // var bind = Function.prototype.bind; // 所以 f1.bind === bind // bind.call(f1, { a: 1 }, 1, 2, 3) // bind.call 接收一个函数 fn this arguments // 返回一个新的函数,调用fn 并传入 this arguments