1
console.log(1<2<3)
console.log(3>2>1)
// 答案与解析
true false
对于运算符>、<,一般的计算从左向右
第一个题:1 < 2 等于 true, 然后true < 3,true == 1 ,因此结果是true
第二个题:3 > 2 等于 true, 然后true > 1, true == 1 ,因此结果是false
2
typeof null
null instanceof Object
1)typeof操作符返回一个字符串,表示未经计算的操作数的类型
类型 结果
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
函数对象 "function"
任何其他对象 "object"
typeof null === 'object';// 从最开始的时候javascript就是这样
JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。这算一个bug,但是被拒绝修复,因为影响的web系统太多
2)instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性
null不是以Object原型创建,因此返回false
3
var END=Math.pow(2,53)
var START = END - 100
var count = 0
for (var i=START;i<=END;i++) {
count++
}
console.log(count)
会死循环 因为END是最大值 永远不会大于
4
var a = Date(0) 字符串
var b = new Date(0) 对象
var c = new Date()
a===b false b===c false a===c false
需要注意的是只能通过调用 Date 构造函数来实例化日期对象:以常规函数调用它(即不加 new 操作符)将会返回一个字符串,而不是一个日期对象。另外,不像其他JavaScript 类型,Date 对象没有字面量格式。
a是字符串,b和c是Date对象,并且b代表的是1970年那个初始化时间,而c代表的是当前时间。
5
var x = 20
var temp = {
x: 40,
foo: function () {
var x = 10
console.log(this.x)
}
}
(temp.foo,temp.foo)()
逗号表达式,从左到右执行,右边的赋值 相当于
(function () {
var x = 10
console.log(this.x)
})() this指向window
6 实现(5).add(3).minus(2)的功能
Number.prototype.add = function (num) {
if (typeof num !== 'number) {
console.log('请输入一个数字')
}
return this + num
}
Number.prototype.minus = function (num) {
if (typeof num !== 'number) {
console.log('请输入一个数字')
}
return this - num
}
7
var a = 1
(function a() {
a= 2
console.log(a)
})()
立即调用的函数表达式(IIFE) 有一个 自己独立的 作用域,如果函数名称与内部变量名称冲突,就会永远执行函数本身;所以上面的结果输出是函数本身;
8
var a = [0]
if (a) {
console.log(a==true)
}else {
console.log(a)
}
输出 false
数组0 转化为 booolean !![0] true
js 比较的规则
当a出现在if的条件中时,被转成布尔值,而Boolean([0])为true,所以就进行下一步判断 a == true,在进行比较时,[0]被转换成了0,所以0==true为false
js的规则是:
如果比较的是原始类型的值,原始类型的值会转成数值再进行比较
1 == true //true 1 === Number(true)
'true' == true //false Number('true')->NaN Number(true)->1
'' == 0//true
'1' == true//true Number('1')->1
对象与原始类型值比较,对象会转换成原始类型的值再进行比较。
undefined和null与其它类型进行比较时,结果都为false,他们相互比较时结果为true。
const a = [1,2,3],
b = [1,2,3],
c = [1,2,4],
d = "2",
e = "11";
console.log([a == b, a === b, a > c, a < c, d > e]);
// 答案
[false,false,false,true,true]
// 解析
1)JavaScript 有两种比较方式:严格比较运算符和转换类型比较运算符。
对于严格比较运算符(===)来说,仅当两个操作数的类型相同且值相等为 true,而对于被广泛使用的比较运算符(==)来说,会在进行比较之前,将两个操作数转换成相同的类型。对于关系运算符(比如 <=)来说,会先将操作数转为原始值,使它们类型相同,再进行比较运算。
当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。
javascript中Array也是对象,所以这里a,b,c显然引用是不相同的,所以这里a==b,a===b都为false。
2)两个数组进行大小比较,也就是两个对象进行比较
当两个对象进行比较时,会转为原始类型的值,再进行比较。对象转换成原始类型的值,算法是先调用valueOf方法;如果返回的还是对象,再接着调用toString方法。
①valueOf() 方法返回指定对象的原始值。
JavaScript调用valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。默认情况下,valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。
②toString() 方法返回一个表示该对象的字符串。
每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。
③经过valueOf,toString的处理,所以这里a,c最终会被转换为"1,2,3"与"1,2,4";
3)两个字符串进行比较大小
上边的数组经转换为字符串之后,接着进行大小比较。
MDN中的描述是这样的:字符串比较则是使用基于标准字典的 Unicode 值来进行比较的。
字符串按照字典顺序进行比较。JavaScript 引擎内部首先比较首字符的 Unicode 码点。如果相等,再比较第二个字符的 Unicode 码点,以此类推。
所以这里 "1,2,3" < "1,2,4",输出true,因为前边的字符的unicode码点都相等,所以最后是比较3和4的unicode码点。而3的Unicode码点是51,4的uniCode码点是52,所以a<c。
"2" > "11"也是同理,这个也是开发中有时会遇到的问题,所以在进行运算比较时需要注意一下。
4)关于valueOf,toString的调用顺序
①javascript中对象到字符串的转换经历的过程如下:
如果对象具有toString()方法,javaScript会优先调用此方法。如果返回的是一个原始值(原始值包括null、undefined、布尔值、字符串、数字),javaScript会将这个原始值转换为字符串,并返回字符串作为结果。
如果对象不具有toString()方法,或者调用toString()方法返回的不是原始值,则javaScript会判断是否存在valueOf()方法,如若存在则调用此方法,如果返回的是原始值,javaScript会将原始值转换为字符串作为结果。
如果javaScript无法调用toString()和valueOf()返回原始值的时候,则会报一个类型错误异常的警告。
比如:String([1,2,3]);将一个对象转换为字符串
var a = [1,2,3];
a.valueOf = function(){
console.log("valueOf");
return this
}
a.toString = function(){
console.log('toString')
return this
}
String(a);
因为这里我返回的是this,最后,所以如果javaScript无法调用toString()和valueOf()返回原始值的时候,则会报一个类型错误异常的警告。
②javaScript中对象转换为数字的转换过程:
javaScript优先判断对象是否具有valueOf()方法,如具有则调用,若返回一个原始值,javaScript会将原始值转换为数字并作为结果。
如果对象不具有valueOf()方法,javaScript则会调用toString()的方法,若返回的是原始值,javaScript会将原始值转换为数字并作为结果。
如果javaScript无法调用toString()和valueOf()返回原始值的时候,则会报一个类型错误异常的警告。
比如:Number([1,2,3]);将一个对象转换为字符串
let a = []
let b = '0'
console.log(a==0)
console.log(a==!a)
console.log(b==0)
console.log(a==b)
答案: true true true false
解析
1 a是对象,需要转化为原始类型
[].valueOf().toString() == 0 =>
'' == 0 ? 这时候都转成数字
Number('') == 0? 为true
2 先计算!a 除了null undefined NaN '' 0 取反为true其他都是 false 于是变成为a == false ? 转成 a == 0 同1
3 字符串会转化成数字 0 所以想等
4 a 转化成 ''
'' != '0'
9
var a =?
if(a == 1 && a == 2 && a ==3){
console.log(1)
}
var a = [1,2,3]
a.join=a.shift
var b = 1
Object.defineProperty(window, 'a', {
get:function(){b++}
})
let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};
if (a == 1 && a == 2 && a == 3) {
console.log('1');
}
/*
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。我们之前在定义类的内部私有属性时候习惯用 __xxx ,这种命名方式避免别人定义相同的属性名覆盖原来的属性,有了 Symbol 之后我们完全可以用 Symbol值来代替这种方法,而且完全不用担心被覆盖。
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。Symbol.toPrimitive就是其中一个,它指向一个方法,表示该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。这里就是改变这个属性,把它的值改为一个 闭包 返回的函数。
*/
10
let a = {n: 1};
let b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
答案:
undefined {n:2}
注意点:
1: 点的优先级大于等号的优先级
2: 对象以指针的形式进行存储,每个新对象都是一份新的存储地址
解析:
var b = a;
b 和 a 都指向同一个地址。.
的优先级高于=
。所以先执行a.x
,于是现在的a
和b
都是{n: 1, x: undefined}
。=
是从右向左执行。所以是执行a = {n: 2}
,于是a
指向了{n: 2}
- 再执行
a.x = a
。 这里注意,a.x
是最开始执行的,已经是{n: 1, x: undefined}
这个地址了,而不是一开的的那个a
,所以也就不是{n: 2}
了。而且b
和旧的a
是指向一个地址的,所以b
也改变了。 - 但是,
=
右面的a,是已经指向了新地址的新a
。 - 所以,
a.x = a
可以看成是{n: 1, x: undefined}.x = {n: 2}
- 最终得出
a = { n: 2 },
b = {
n: 1,
x: { n: 2 }
}
11 实现一个模板引擎
let template = '我是{{name}},年龄{{age}},性别{{sex}}'
let data = {name: '姓名', age: '13', sex: 14}
render(template,data)
function render(template, data) {
var reg = /{{(w+)}}/
if (reg.test(template)) {
const exec = reg.exec(template)
template = template.replace(exec[0], data[exec[1]])
return render(template, data)
}
return template
}
12 什么是包装对象
// 答案
1)复习一下JS的数据类型,JS数据类型被分为两大类,基本类型和引用类型
①基本类型:Undefined,Null,Boolean,Number,String,Symbol,BigInt
②引用类型:Object,Array,Date,RegExp等,说白了就是对象。
2)其中引用类型有方法和属性,但是基本类型是没有的,但我们经常会看到下面的代码
let name = "marko";
console.log(typeof name); // "string"
console.log(name.toUpperCase()); // "MARKO"
name类型是 string,属于基本类型,所以它没有属性和方法,但是在这个例子中,我们调用了一个toUpperCase()方法,它不会抛出错误,还返回了对象的变量值。
原因是基本类型的值被临时转换或强制转换为对象,因此name变量的行为类似于对象。 除null和undefined之外的每个基本类型都有自己包装对象。也就是:String,Number,Boolean,Symbol和BigInt。 在这种情况下,name.toUpperCase()在幕后看起来如下:
console.log(new String(name).toUpperCase()); // "MARKO"
在完成访问属性或调用方法之后,新创建的对象将立即被丢弃。
13
function yideng(){}
const a = {}
b=Object.prototype
console.log(a.prototype===b) false prototype是函数才会有的
console.log(Object.getPrototypeOf(a)===b) true
console.log(yideng.prototype === Object.getPrototypeOf(yideng)) false
//解析:
1)a.prototype === b =>false
prototype属性是只有函数才特有的属性,当你创建一个函数时,js会自动为这个函数加上prototype属性,值是一个空对象。而实例对象是没有prototype属性的。所以a.prototype是undefined,第一个结果为false。
2)Object.getPrototypeOf(a) === b =>true
首先要明确对象和构造函数的关系,对象在创建的时候,其__proto__会指向其构造函数的prototype属性
Object实际上是一个构造函数(typeof Object的结果为"function"),使用字面量创建对象和new Object创建对象是一样的,所以a.__proto__也就是Object.prototype,所以Object.getPrototypeOf(a)与a.__proto__是一样的,第二个结果为true
3)yideng.prototype === Object.getPrototypeOf(yideng) =>false
关键点:f.prototype和Object.getPrototypeOf(f)说的不是一回事
①f.prototype 是使用使用 new 创建的 f 实例的原型:
f.prototype === Object.getPrototypeOf(new f()); // true
②Object.getPrototypeOf(f)是 f 函数的原型:
Object.getPrototypeOf(f) === Function.prototype; //true
所以答案是 false
//知识点
__proto__(隐式原型)与prototype(显式原型)
1)是什么?
①显式原型 explicit prototype property:
每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。(需要注意的是,通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性)
②隐式原型 implicit prototype link:
JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf()。(注意:Object.prototype 这个对象是个例外,它的__proto__值为null)
③二者的关系:
隐式原型指向创建这个对象的函数(constructor)的prototype
2)作用是什么?
①显式原型的作用:用来实现基于原型的继承与属性的共享。
②隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。
3)__proto__的指向:
__proto__的指向到底如何判断呢?根据ECMA定义 'to the value of its constructor’s "prototype" ' ----指向创建这个对象的函数的显式原型。
所以关键的点在于找到创建这个对象的构造函数,接下来就来看一下JS中对象被创建的方式,一眼看过去似乎有三种方式:(1)对象字面量的方式 (2)new 的方式 (3)ES5中的Object.create()。
但是本质上只有一种方式,也就是通过new来创建。为什么这么说呢,首先字面量的方式是一种为了开发人员更方便创建对象的一个语法糖,本质就是 var o = new Object(); o.xx = xx;o.yy=yy;
14
// 来一道面试题
var a=10;
var foo={
a:20,
bar:function(){
var a=30;
return this.a;
}
}
console.log(foo.bar());
console.log((foo.bar)());
console.log((foo.bar=foo.bar)());
console.log((foo.bar,foo.bar)());
// 答案:
20 20 10 10
// 第一问 foo.bar()
/*
foo调用,this指向foo , 此时的 this 指的是foo,输出20
*/
// 第二问 (foo.bar)()
/*
给表达式加了括号,而括号的作用是改变表达式的运算顺序,而在这里加与不加括号并无影响;相当于foo.bar(),输出20
*/
// 第三问 (foo.bar=foo.bar)()
/*
等号运算,
相当于重新给foo.bar定义,即
foo.bar = function () {
var a = 10;
return this.a;
}
就是普通的复制,一个匿名函数赋值给一个全局变量
所以这个时候foo.bar是在window作用域下而不是foo = {}这一块级作用域,所以这里的this指代的是window,输出10
*/
// 第四问 (foo.bar,foo.bar)()
/*
1.逗号运算符,
2.逗号表达式,求解过程是:先计算表达式1的值,再计算表达式2的值,……一直计算到表达式n的值。最后整个逗号表达式的值是表达式n的值。逗号运算符的返回值是最后一个表达式的值。
3.其实这里主要还是经过逗号运算符后,就是纯粹的函数了,不是对象方法的引用,所以这里this指向的是window,输出10
4.第三问,第四问,一个是等号运算,一个是逗号运算,可以这么理解,经过赋值,运算符运算后,都是纯粹的函数,不是对象方法的引用。所以函数指向的this都是windows的。
*/
如果用一句话说明 this 的指向,那么即是: 谁调用它,this 就指向谁。但是仅通过这句话,我们很多时候并不能准确判断 this 的指向。因此我们需要借助一些规则去帮助自己:
首先来看一下this绑定的规则,来详细看一下,这样再遇到this的问题,可以从容应对
默认绑定
// 默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是独立函数调用。
function sayHi(){console.log('Hello,', this.name);
}
var name = 'yideng';
sayHi();
//在调用Hi()时,应用了默认绑定,this指向全局对象(非严格模式下),严格模式下,this指向undefined,undefined上没有this对象,会抛出错误。
// 如果在浏览器环境中运行,那么结果就是 Hello,yideng
// 如果在node环境中运行,结果就是 Hello,undefined.这是因为node中name并不是挂在全局对象上的。隐式绑定
// 函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的形式为 XXX.fun()
function sayHi(){console.log('Hello,', this.name);
}
var person = {name: 'yidneg1', sayHi: sayHi
}
var name = 'yidneg2';
person.sayHi();
// Hello yideng1
// sayHi函数声明在外部,严格来说并不属于person,但是在调用sayHi时,调用位置会使用person的上下文来引用函数,隐式绑定会把函数调用中的this(即此例sayHi函数中的this)绑定到这个上下文对象(即此例中的person)需要注意的是:对象属性链中只有最后一层会影响到调用位置。
function sayHi(){ console.log('Hello,', this.name); } var person2 = { name: 'yideng1', sayHi: sayHi } var person1 = { name: 'yideng2', friend: person2 } person1.friend.sayHi();
// Hello yideng1
//因为只有最后一层会确定this指向的是什么,不管有多少层,在判断this的时候,我们只关注最后一层,即此处的friend。隐式绑定有一个大陷阱,绑定很容易丢失(或者说容易给我们造成误导,我们以为this指向的是什么,但是实际上并非如此).
function sayHi(){ console.log('Hello,', this.name); } var person = { name: 'yideng1', sayHi: sayHi } var name = 'yideng2'; var Hi = person.sayHi; Hi();
// Htllo yideng2
// Hi直接指向了sayHi的引用,在调用的时候,跟person没有半毛钱的关系,针对此类问题,我建议大家只需牢牢记住这个格式:XXX.fn();fn()前如果什么都没有,那么肯定不是隐式绑定。- 除了上面这种丢失之外,隐式绑定的丢失是发生在回调函数中(事件回调也是其中一种),
function sayHi(){ console.log('Hello,', this.name); } var person1 = { name: 'yideng1', sayHi: function(){ setTimeout(function(){ console.log('Hello,',this.name); }) } } var person2 = { name: 'yideng2', sayHi: sayHi } var name='yideng3'; person1.sayHi(); setTimeout(person2.sayHi,100); setTimeout(function(){ person2.sayHi(); },200); // Hello yideng3 // Hello yideng3 // Hello yideng2
1.第一条输出很容易理解,setTimeout的回调函数中,this使用的是默认绑定,非严格模式下,执行的是全局对象
2.第二条输出是不是有点迷惑了?说好XXX.fun()的时候,fun中的this指向的是XXX呢,为什么这次却不是这样了!Why?
其实这里我们可以这样理解: setTimeout(fn,delay){ fn(); },相当于是将person2.sayHi赋值给了一个变量,最后执行了变量,这个时候,sayHi中的this显然和person2就没有关系了。
3.第三条虽然也是在setTimeout的回调中,但是我们可以看出,这是执行的是person2.sayHi()使用的是隐式绑定,因此这是this指向的是person2,跟当前的作用域没有任何关系。- 看到这里是不是有点疲倦了
显示绑定
- 显式绑定比较好理解,就是通过call,apply,bind的方式,显式的指定this所指向的对象。
- call,apply和bind的第一个参数,就是对应函数的this所指向的对象。call和apply的作用一样,只是传参方式不同。call和apply都会执行对应的函数,而bind方法不会。
function sayHi(){ console.log('Hello,', this.name); } var person = { name: 'yideng1', sayHi: sayHi } var name = 'yideng2'; var Hi = person.sayHi; Hi.call(person); //Hi.apply(person) // Hello yideng1 因为使用硬绑定明确将this绑定在了person上。
- 使用了硬绑定,是不是意味着不会出现隐式绑定所遇到的绑定丢失呢?显然不是这样的,不信,继续往下看。
function sayHi(){ console.log('Hello,', this.name); } var person = { name: 'yideng1', sayHi: sayHi } var name = 'yideng2'; var Hi = function(fn) { fn(); } Hi.call(person, person.sayHi);
// Hello yideng2
输出的结果是 Hello, Wiliam. 原因很简单,Hi.call(person, person.sayHi)的确是将this绑定到Hi中的this了。但是在执行fn的时候,相当于直接调用了sayHi方法(记住: person.sayHi已经被赋值给fn了,隐式绑定也丢了),没有指定this的值,对应的是默认绑定。
现在,我们希望绑定不会丢失,要怎么做?很简单,调用fn的时候,也给它硬绑定。var Hi = function(fn) {
fn.call(this);
}
这样就行了
因为person被绑定到Hi函数中的this上,fn又将这个对象绑定给了sayHi的函数。这时,sayHi中的this指向的就是person对象。new 绑定
- javaScript和C++不一样,并没有类,在javaScript中,构造函数只是使用new操作符时被调用的函数,这些函数和普通的函数并没有什么不同,它不属于某个类,也不可能实例化出一个类。任何一个函数都可以使用new来调用,因此其实并不存在构造函数,而只有对于函数的“构造调用”
前边我们提到new 操作符都干了什么
- 创建一个空对象,构造函数中的this指向这个空对象
- 这个新对象被执行 [[原型]] 连接
- 执行构造函数方法,属性和方法被添加到this引用的对象中
- 如果构造函数中没有返回其它对象,那么返回this,即创建的这个的新对象,否则,返回构造函数中返回的对象。
因此,我们使用new来调用函数的时候,就会新对象绑定到这个函数的this上。
function sayHi(name){ this.name = name; } var Hi = new sayHi('yideng'); console.log('Hello,', Hi.name); // Hello yideng
绑定优先级
- 我们知道了this有四种绑定规则,但是如果同时应用了多种规则,怎么办?
显然,我们需要了解哪一种绑定方式的优先级更高,这四种绑定的优先级为:
- new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
- 感兴趣的可以写个demo测试看看
绑定例外
- 凡事都有例外,this的规则也是这样。
- 如果我们将null或者是undefined作为this的绑定对象传入call、apply或者是bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
var foo = { name: 'yideng1' } var name = 'yideng2'; function bar() { console.log(this.name); } bar.call(null); //yideng2 因为这时实际应用的是默认绑定规则。
箭头函数
箭头函数是ES6中新增的,它和普通函数有一些区别,箭头函数没有自己的this,它的this继承于外层代码库中的this。箭头函数在使用时,需要注意以下几点:
- 函数体内的this对象,继承的是外层代码块的this。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
- 箭头函数没有自己的this,所以不能用call()、apply()、bind()这些方法去改变this的指向.
15 手撸代码
防抖与节流介绍下原理 并手动实现一下
1)防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
适用场景:
①按钮提交场景:防止多次提交按钮,只执行最后提交的一次
②服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似
const debounce = (fn,delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this.args);
},delay)
}
}
节流单位时间只执行一次
// 手写简化版实现
// ①定时器实现
const throttle = (fn,delay = 500) =>{
let flag = true;
return (...args) => {
if(!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this,args);
flag = true;
},delay);
};
}
// ②时间戳实现
const throttle = (fn,delay = 500) => {
let preTime = Date.now();
return (...args) => {
const nowTime = Date.now();
if(nowTime - preTime >= delay){
preTime = Date.now();
fn.apply(this,args);
}
}
}
new 的实现原理,模拟实现一下
// 首先,new操作符做了什么
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new 关键字会进行如下的操作
创建一个空的简单JavaScript对象(即{});
链接该对象(即设置该对象的构造函数)到另一个对象 ;
将步骤1新创建的对象作为this的上下文 ;
如果该函数没有返回对象,则返回this。即创建的这个的新对象,否则,返回构造函数中返回的对象。
function newOperate (...args) {
if (typeof ctor !== 'function'){
throw 'newOperator function the first param must be a function';
}
// ES5 arguments转成数组 当然也可以用ES6 [...arguments], Aarry.from(arguments);
var args = Array.prototype.slice.call(arguments, 1);
// 1.创建一个空的简单JavaScript对象(即{})
var obj = {};
// 2.链接该新创建的对象(即设置该对象的__proto__)到该函数的原型对象prototype上
obj.__proto__ = ctor.prototype;
// 3.将步骤1新创建的对象作为this的上下文
var result = ctor.apply(obj, args);
// 4.如果该函数没有返回对象,则返回新创建的对象
var isObject = typeof result === 'object' && result !== null;
var isFunction = typeof result === 'function';
return isObject || isFunction ? result : obj;
}
高频知识点
call/apply/bind
Promise
深拷贝、浅拷贝
垃圾回收
EventLoop
缓存相关
http相关
浏览器从加载页面到渲染过程
回答注意
抓住核心的要点
把要点说全面
再稍微加一些解析
要简明扼要 思路清晰 不能拖沓
安全
什么是XSS 攻击 如何进行防范
什么是CSRF攻击 如何进行防范
算法
给定一个只包括 '(' ')' '[',']','{','}'的字符串,判断字符串是否 字符串需满足: 1 左括号必须用相同类型的右括号闭合 2 左括号必须以正确的顺序闭合
介绍项目背景
承担的一个角色
项目的结果 收益
项目总结和反思
16 最长不重复子序列
var s = 'bcdabd134waddd'
var s1 = s[0]
var set = new Set([s[0]])
var i = 0
var j = 1
while(i<s.length&&j<s.length) {
if(set.has(s[j])) {
set.delete(s[i])
i++
}else{
set.add(s[j])
j++
if (s1.length < set.size) {
s1 = s.slice(i, j)
}
}
}
console.log(s1)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。