Javascript 的原型和继承
JavaScript 对象有一个指向一个原型对象的链。 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。 函数的继承与其他的属性继承没有差别。需要注意的是,当继承的函数被调用时,this指向的是当前继承的对象,而不是继承的函数所在的原型对象。
如何理解 Javascript 中的“一切皆对象”
当谈到继承时,JavaScript 只有一种结构:对象。 每个实例对象object都有一个私有属性__proto__指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象__proto__,层层向上直到一个对象的原型对象为 null。null 没有原型,并作为这个原型链中的最后一个环节。
如何创建一个对象
可以使用以下方法: 使用语法结构创建的对象,即定义一个数组、函数、对象等 使用构造器创建的对象: new XXX() 使用Object.create创建对象 使用class关键字创建对象
proto 与 prototype 的区别
JavaScript 可以通过prototype和__proto__在两个对象之间创建一个关联,使得一个对象就可以通过委托访问另一个对象的属性和函数。当我们创建函数时,Javascript 会为这个函数自动添加prototype属性,值是一个有constructor属性的对象。一旦我们通过new关键字调用,那么 Javascript 就会帮你创建该构造函数的实例,实例通过将__proto__指向承构造函数的prototype,来继承构造函数prototype的所有属性和方法。 对象__proto__属性的值就是它所对应的原型对象,指向自己构造函数的prototype。每个对象都有__proto__属性来标识自己所继承的原型,但只有函数才有prototype属性。
如何判断对象类型?
typeof运算符,返回值包括但不限于: undefined, boolean, number, string, object。问题:容易混淆object和null,会把Array还有用户自定义函数都返回为object。 instanceof运算符,判断某一个对象是否是所给的构造函数的一个实例。 constructor属性,返回对创建此对象的数组函数的引用。
作用域与闭包
www.ruanyifeng.com/blog/2009/0…
请描述以下代码的执行输出内容(考察作用域)
什么场景需要使用闭包
由于 Javascript 特殊的变量作用域,函数内部可以直接读取全局变量,但在函数外部自然无法读取函数内的局部变量。闭包用途:
- 用于从外部读取其他函数内部变量的函数
- 可以使用闭包来模拟私有方法
- 让这些变量的值始终保持在内存中(涉及垃圾回收机制,可能导致内存泄露问题)
闭包的缺陷
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
this
简单描述 this 在不同场景下的指向
Javascript 中 this指针代表的是执行当前代码的对象的所有者,可简单理解为this永远指向最后调用它的那个对象。 根据 JavaScript 中函数的调用方式不同,分为以下情况:
- 函数作为对象的方法调用:此时
this被自然绑定到该对象。 - 作为函数调用: 函数可以直接被调用,此时
this绑定到全局对象(在浏览器中为 window)。 - 作为构造函数调用:如果不使用
new调用,则和普通函数一样。 - 使用
apply、call、bind等方式调用:根据API不同,可切换函数执行的上下文环境,即this绑定的对象。
apply 和 call 的使用
apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。
call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
bind除了返回是函数以外,它的参数和call一样。
通过apply和call改变函数的this指向,他们两个函数的第一个参数都是一样的表示要改变指向的那个对象,第二个参数,apply是数组,而call则是arg1,arg2...这种形式。通过bind改变this作用域会返回一个新的函数,这个函数不会马上执行。
返回不同:bind返回是函数 参数不同:apply(A, arguments),bind(A, args1,args2)
箭头函数与普通函数的区别
箭头函数的this始终指向函数定义时的this,而非执行时。
var a=11;
function test2(){
this.a=22;
let b=()=>{console.log(this.a)}
b();
}
var x=new test2();
//输出22
定义时绑定。
ES6+
对 Promise 的理解
Javascript 的单线程设计,导致网络请求、事件监听等都需要异步执行。异步执行通常用回调函数实现,多层的回调会导致回调金字塔的出现。 Promise 允许我们为异步操作的成功和失败分别绑定相应的处理方法(handlers),同时可以通过Promise.all()、Promise.race()等方法来同时处理多个异步操作的结果。
Promise对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。 f1().then(f2);
一个promise可能有三种状态:等待(pending)、已完成(resolved,又称fulfilled)、已拒绝(rejected)。
promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致。
then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
使用 async、await 的好处
相比直接使用Promise来说,优势在于处理then的调用链,能够更清晰准确的写出代码,毕竟写一大堆then也很恶心,并且也能优雅地解决回调地狱问题。当然也存在一些缺点,因为await将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了await会导致性能上的降低。
浏览器兼容性与 Babel
Set 和 Map 数据结构
代码考察
手写代码实现 call/apply/bind
call方法实现:
Function.prototype.mycall = function(context,...arg){ // Function是所有函数对象的构造函数。这里给Function的原型声明了一个mycall方法,则所有的函数都拥有了这个mycall方法
const temp = Symbol("unique symbol") // symbol代表标识唯一性的ID(就算每次参数是unique symbol,但是temp都不相等)
context[temp] = this // 这里的this 代表调用mycall的函数。this的指向:被谁调用,this就指向谁。这里的context就是传递的obj2。可以理解为obj2.fn = obj1.fn
context[temp](...arg) // 这里通过展开运算符传递参数。展开运算符是传递参数的一种简写。调用fn函数
delete context[temp] // 这时将obj2.fn删除。一定要删除,因为obj2本身是没有这个函数的,只是在传递作用域调用时,做一个临时属性
}
let obj1 = {
name: "obj1",
fn: function(a,b,c){
console.log(this.name+": "+ a + b + c)
}
}
let obj2 = {
name: "obj2",
}
obj1.fn.mycall(obj2,4,5,6)
apply方法实现:
Function.prototype.myApply = function (context, arg) { // apply和call实际上只有参数传递上的区别,传递的第二个参数是一个数组
const fn = Symbol('临时属性')
context[fn] = this
context[fn](...arg)
delete context[fn]
}
let obj1 = {
name: "obj1",
fn: function(a,b,c){
console.log(this.name+": "+ a + b + c)
}
}
let obj2 = {
name: "obj2",
}
obj1.fn.myApply(obj2,[1,2,3])
bind方法实现:
Function.prototype.myBind = function (context, ...firstarg) {
const that = this // 当前调用myBind的函数
const bindFn = function (...secoundarg) { // bind的本质是将call方法封装成函数return 执行这个函数则调用call方法
return that.mycall(context, ...firstarg, ...secoundarg) // 调用bind的时候可能传一次参,调用bind返回的函数可能会再一次传参。
}
bindFn.prototype = Object.create(that.prototype) // 复制源函数的prototype给fToBind
return bindFn
}
let obj1 = {
name: "obj1",
fn: function (...arg) {
console.log(this.name + ": " + arg)
}
}
let obj2 = {
name: "obj2",
}
let fnAfterBind = obj1.fn.myBind(obj2, 1, 2, 3)
fnAfterBind(4, 5, 6)
手写代码实现 Promise、async/await
Promise的基本用法,
let promise1 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok')
},1000)
})
promise1.then(function success(val){
console.log(val)
})
最简单代码实现promise
class PromiseM {
constructor (process) {
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this), this.reject.bind(this))
return this
}
resolve (val) {
this.status = 'fulfilled'
this.msg = val
}
reject (err) {
this.status = 'rejected'
this.msg = err
}
then (fufilled, reject) {
if(this.status === 'fulfilled') {
fufilled(this.msg)
}
if(this.status === 'rejected') {
reject(this.msg)
}
}
}
//测试代码
| 12345678 | var mm=new PromiseM(function(resolve,reject){``resolve(``'123'``);``});``mm.then(function(success){``console.log(success);``},function(){``console.log(``'fail!'``);``}); |
|---|
(async () = > {
await new promise();
})()
Javascript 中 0.1+0.2 为什么等于 0.30000000000000004,如何通过代码解决这个问题
数字运算中的精度缺失 将浮点数转化成整数计算