在三大框架盛行的时代, 基本上会个Vue
就能在小公司浑水摸鱼。可是当想突破的时候就会意识到基础的重要性。前端
JavaScript
中有不少重要特性及概念。好比原型,原型链,this,闭包,做用域,隐式转换等等。若是不能熟练掌握,在进阶中级前端开发工程师的道路上一定是困难重重。git
用一个小时把这些题作完。检测一下你的基础掌握程度。github
if(false){ var a = 1; let b = 2; } console.log(a); console.log(b);
var a = 1; if(true){ console.log(a); let a = 2; }
var a = {n: 1} var b = a a.x = a = {n: 2} console.log(a.n, b.n); console.log(a.x, b.x);
console.log(c); var c; function c(a) { console.log(a); var a = 3; function a(){ } } c(2);
var c = 1 function c(c) { console.log(c); var c = 3; } console.log(c); c(2);
var name = 'erdong'; (function () { if (typeof name === 'undefined') { var name = 'chen' console.log(name) } else { console.log(name) } })();
var a = 10; function test() { a = 100; console.log(a); console.log(this.a); var a; console.log(a); } test();
if (!(a in window)) { var a = 1; } console.log(a)
var a = 1 function c(a, b) { console.log(a) a = 2 console.log(a) } c()
var val=1; var obj={ val:2, del:function(){ console.log(this); this.val*=2 console.log(val) } } obj.del();
var name = "erdong" var object = { name: "chen", getNameFunc: function () { return function () { return this.name; } } } console.log(object.getNameFunc()());
var name = "erdong" var object = { name: "chen", getNameFunc: function () { var that = this; return function () { return that.name; } } } console.log(object.getNameFunc()());
(function() { var a = b = 3; })(); console.log(typeof a === 'undefined'); console.log(typeof b === 'undefined');
var a = 6; setTimeout(function () { a = 666; }, 0) console.log(a);
function fn1() { var a = 2 function fn2 () { a++ console.log(a) } return fn2 } var f = fn1() f() f()
var a = (function(foo){ return typeof foo.bar; })({foo:{bar:1}}); console.log(a);
function f(){ return f; } console.log(new f() instanceof f);
function A () { } A.prototype.n = 1 var b = new A(); A.prototype = { n: 2, m: 3 } var c = new A() console.log(b.n, b.m); console.log(c.n, c.m);
var F = function(){} var O = {}; Object.prototype.a = function(){ console.log('a') } Function.prototype.b = function(){ console.log('b') } var f = new F() F.a(); F.b(); O.a(); O.b();
function Person() { getAge = function () { console.log(10) } return this } Person.getAge = function () { console.log(20) } Person.prototype.getAge = function () { console.log(30) } var getAge = function () { console.log(40) } function getAge() { console.log(50) } Person.getAge(); getAge(); Person().getAge(); new Person.getAge(); getAge(); new Person().getAge();
console.log(false.toString()); console.log([1, 2, 3].toString()); console.log(1.toString()); console.log(5..toString());
console.log(typeof NaN === 'number');
console.log(1 + "2" + "2"); console.log(1 + +"2" + "2"); console.log(1 + -"1" + "2"); console.log(+"1" + "1" + "2"); console.log( "A" - "B" + "2"); console.log( "A" - "B" + 2);
var a = 666; console.log(++a); console.log(a++);
console.log(typeof a); function a() {} var a; console.log(typeof a);
var a; var b = 'undefined'; console.log(typeof a); console.log(typeof b); console.log(typeof c);
var x = 1; if(function f(){}){ x += typeof f; } console.log(x);
var str = "123abc"; console.log(typeof str++);
console.log('b' + 'a' + +'a'+'a');
var obj = {n: 1}; function fn2(a) { a.n = 2; } fn2(obj); console.log(obj.n);
var x = 10; function fn() { console.log(x); } function show(f) { var x = 20; f(); } show(fn);
Object.prototype.bar = 1; var foo = { goo: undefined }; console.log(foo.bar); console.log('bar' in foo); console.log(foo.hasOwnProperty('bar')); console.log(foo.hasOwnProperty('goo'));
Object.prototype.bar = 1; var foo = { moo: 2 }; for(var i in foo) { console.log(i); }
function foo1() { return { bar: "hello" }; } function foo2() { return { bar: "hello" }; } console.log(foo1()); console.log(foo2());
console.log((function(){ return typeof arguments; })());
console.log(Boolean(false)); console.log(Boolean('0')); console.log(Boolean('')); console.log(Boolean(NaN));
console.log(Array(3)); console.log(Array(2,3));
console.log(0.1 + 0.2 == 0.3);
var a=[1, 2, 3]; console.log(a.join());
var a = [3]; var b = [1]; console.log(a - b);
// 输出 undefined ReferenceError: b is not defined
var
不会产生块级做用域,let
会产生块级做用域。数组
示例代码至关于:闭包
var a = 1; if(false){ a = 1; let b = 2; } console.log(a); console.log(b);
// 输出 ReferenceError: Cannot access 'a' before initialization
let
声明的变量不会提高,而且会产生暂存死区。在let
声明变量以前访问变量会抛出错误。框架
// 输出 2 1 undefined {n: 2}
var b = a,此时a和b指向同一个对象。 .运算符比 = 运算符高,先计算`a.x`,此时 b = { n:1, x:undefined } 至关于给对象添加了x属性。 a.x = a = {n:2}; 计算完a.x,再计算 = ,赋值是从右向左,此时a指向一个新对象。 a = { n:2 } a.x已经执行过了,此时对象的x属性赋值为a,此时 对象 = { n:1, x:{ n:2 } } 即: a = { n:2 } b = { n:1, x:{ n:2 } }
查看运算符优先级函数
// 输出 function c(){ console.log(a); var c = 3; function a(){ } } function a(){ }
变量提高也有优先级, 函数声明 > arguments > 变量声明学习
// 输出 1 TypeError: c is not a function
因为函数声明会提高,当函数外的console.log(c)
执行时,c
已经被赋值为1
。所以,执行c(2)
时会抛出TypeError
,由于1
不是函数。this
// 输出 chen
自执行函数执行时,会先进行变量提高(这里涉及到执行上下文不过多说,必定要搞懂执行上下文),在自执行函数执行时,伪代码为:spa
var name = 'erdong'; (function () { var name; // 变量name会提高到当前做用域顶部 if (typeof name === 'undefined') { name = 'chen' console.log(name) } else { console.log(name) } })();
因此会执行if
中的console.log(name)
// 输出 100 10 100
test()
为函数独立调用,做用域中的this
绑定为全局对象window
。
test
函数执行时,var a
被提高到了做用域顶部,所以函数做用域中存在一个变量a
。因此在函数中访问的a
都是局部做用域中的a
。
// 输出 undefined
因为if
后的{}
不会产生块级做用域(不包含let,const时),此时的伪代码为:
var a; if (!(a in window)) { a = 1; } console.log(a);
var a
至关于window.a
。所以!(a in window)
转成布尔值为false
,不会执行a = 1
。全部console.log(a)
输出undefined
。
//输出 undefined 2
跟第4题相似。
// 输出 obj(指向的值) 1
当经过obj.del()
调用del
函数时,del
函数做用域中的this
绑定为obj
。
在函数做用域中访问val
时,因为函数中并无变量val
,所以实际上访问的是全局做用域中的val
,即 1
。
这里考察的是this
的指向,必定要熟练掌握。
// 输出 erdong
object.getNameFunc()()
,先执行object.getNameFunc()
返回一个函数:
function () { return this.name; }
返回的函数再执行,至关于
(function () { return this.name; })();
此时的this
绑定为window
。所以输出全局变量name
的值erdong
。
//输出 chen
object.getNameFunc()
执行时,此时getNameFunc
中的this
绑定为object
,所以that = object
。object.getNameFunc()
返回的函数再执行时,产生闭包,所以返回的函数也能访问到外层做用域中的变量that
,所以object.name
为object.name
,即 chen
。
// 输出 true false
首先要明白a = b = 3
是怎样执行的,伪代码:
b = 3; var a = b;
所以在自执行函数执行时,b
因为为经var
等操做符声明,由于为全局变量。a
为函数做用域中的变量。所以在外面访问a
和b
时,其值分别为ReferenceError: a is not defined
和3
。可是typeof
检测未声明的变量不会抛出错误,会返回'undefined'
。所以typeof a
和typeof b
分别返回'undefined'
和'number'
//输出 6
setTimeout
为宏任务。即便设置延迟为0ms
,也是等待微任务执行完才会执行。所以console.log(a)
输出 6
// 输出 3 4
因为fn1
函数执行后返回函数fn2
,此时产生了闭包。所以fn2
中a
访问的是fn1
做用域中的变量a
,所以第一次a++
,以后a
为3
,第二次以后a
为4
。
//输出 undefined
实参foo
的值为{foo:{bar:1}
,所以typeof foo.bar
为undefined
。
typeof foo.foo.bar
为number
。
//输出 false
因为构造函数f
的返回值为f
。所以new f()
的值为f
。因此console.log(new f() instanceof f)
为console.log(f instanceof f)
,即 false
。
// 输出 1,undefined 2,3
var b = new A();
实例化b
时,A
的prototype
为
A.prototype = { constructor:A, n:1 }
当访问b.n
和b.m
时,经过原型链找到A.prototype
指向的对象上,即b.n = 1
,b.m = undefined
。
var c = new A();
实例化c
时,A
的prototype
为
A.prototype = { n: 2, m: 3 }
当访问a.n
和a.m
时,经过原型链找到A.prototype
指向的对象上,此时A.prototype
重写,所以a.n = 2
,b.m = 3
。
// 输出 a b a TypeError: o.b is not a function
F
为函数,它也能访问Object
原型上的方法,O
为对象,不能访问Function
原型上的方法。
F
的原型链为:
F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype
因为Object.prototype
在F
的原型链上,因此F
能访问Object.prototype
上的属性和方法。即: F.a()
,F.b()
能正常访问。
O
的原型链为:
O => O.__proto__ => Object.prototype
因为Function.prototype
不在O
的原型链上,所以O
不能访问Function.prototype
上的方法,即O.b()
抛出错误。
若是你对原型和原型链掌握的好,试着理解下面的示例:
console.log(Object instanceof Function); console.log(Function instanceof Object); console.log(Function instanceof Function);
// 输出 20 40 10 20 10 30
Person.getAge();
此时执行的是Person
函数上getAge
方法。
Person.getAge = function () { console.log(20) }
因此输出:20。
getAge();
此时执行的是全局中的getAge
方法。此时全局getAge
方法为:
function () { console.log(40) }
因此输出:40。
Person().getAge();
因为Person()
单独执行因此,做用域中的this
绑定为window
,至关于window..getAge()
。同上,执行的都是全局getAge
方法,可是Person
执行时,内部执行了
getAge = function () { console.log(10) }
所以全局getAge
方法如今为:
function () { console.log(10) }
因此输出:10。
new Person.getAge();
此时至关于实例化Person.getAge
这个函数,伪代码:
var b = Person.getAge; new b();
因此输出:20
getAge();
执行全局getAge
方法,因为在Person().getAge()
执行时把全局getAge
方法赋值为:
function () { console.log(10) }
因此输出:10。
new Person().getAge();
此时调用的是Person
原型上的getAge
方法:
Person.prototype.getAge = function () { console.log(30) }
因此输出:30。
这里要注意:1.变量提高及提高后再赋值。2.调用构造函数时,带()
和不带()
的区别。
// 输出 'false' '1,2,3' Uncaught SyntaxError: Invalid or unexpected token '5'
当执行1.toString();
时,因为1.
也是有效数字,所以此时变成(1.)toString()
。没有用.
调用toString
方法,所以抛出错误。
正确的应该是:
1..toString(); 1 .toString(); (1).toString();
//输出 true
NaN
为不是数字的数字。虽然它不是数字,可是它也是数字类型。
//输出 '122' '32' '02' '112' 'NaN2' NaN
首先要明白两点:
+a
,会把a
转换为数字。-a
会把a
转换成数字的负值(若是能转换为数字的话,不然为NaN
)。console.log(1 + "2" + "2");
简单的字符串拼接,即结果为:'122'
。
console.log(1 + +"2" + "2");
这里至关于console.log(1 + 2 + "2");
,而后再字符串拼接。即结果为:'32'
。
console.log(1 + -"1" + "2");
这里至关于console.log(1 + -1 + "2");
,而后再字符串拼接。即结果为:'02'
。
console.log(+"1" + "1" + "2");
这里至关于console.log(1 + "1" + "2");
,而后再字符串拼接。即结果为:'112'
。
console.log( "A" - "B" + "2");
,因为'A' - 'B' = NaN
,因此至关于console.log( NaN + "2");
, 而后再字符串拼接。即结果为:'NaN2'
。
console.log( "A" - "B" + 2);
同上,至关于console.log(NaN + 2)
,因为NaN
+任何值仍是NaN
,即结果为:NaN
。
// 输出 666 668
a++
先执行取值操做,在执行+1
。 此时输出666
,随后a
的值变为667
。
++a
先执行+1
在执行取值操做。 此时a
的值为667 + 1 = 668
。
--a
和a--
同理。
使用这类运算符时要注意:
1)这里的++
、--
不能用做于常量。好比
1++; // 抛出错误
2)若是a
不是数字类型,会首先经过Number(a)
,将a
转换为数字。再执行++
等运算。
// 输出 'function' 'function'
跟第4题相似。函数会优先于变量声明提早。所以会忽略var a
。
// 输出 'undefined' 'string' 'undefined'
a
为声明未赋值,默认为undefined
,b
的值为字符串'undefined'
,c
为未定义。
typeof
一个未定义的变量时,不会抛出错误,会返回'undefined'
。注意typeof
返回的都是字符串类型。
//输出 1undefined
function f(){}
当作if
条件判断,其隐式转换后为true
。可是在()
中的函数不会声明提高,所以f
函数在外部是不存在的。所以typeof f = 'undefined'
,因此x += typeof f
,至关于x = x + 'undefined'
为'1undefined'
// 输出 'number'
在24题解析时提到,使用++
运算符时(不管是前置仍是后置),若是变量不是数字类型,会首先用Number()
转换为数字。所以typeof str++
至关于typeof Number(str)++
。因为后置的++
是先取值后计算,所以至关于typeof Number("123abc")
。即typeof NaN
,因此输出'number'
。
// 输出 baNaNa
'b' + 'a' + +'a'+'a'
至关于'ba' + +'a'+'a'
,+'a'
会将'a'
转换为数字类型,即+'a' = NaN
。因此最终获得'ba' + NaN +'a'
,经过字符串拼接,结果为:baNaNa
// 输出 4 2 2
函数传递参数时,若是是基本类型为值传递,若是是引用类型,为引用传递。所以实参a
和obj
指向对象的一个引用。当执行a.n
,实际上共同引用的对象修改了,添加了个n
属性,所以obj.n
为2
。
// 输出 10
JavaScript
采用的是词法做用域,它规定了函数内访问变量时,查找变量是从函数声明的位置向外层做用域中查找,而不是从调用函数的位置开始向上查找。所以fn
函数内部访问的x
是全局做用域中的x
,而不是show
函数做用域中的x
。
//输出 1 true false true
in
操做符:检测指定对象(右边)原型链上是否有对应的属性值。hasOwnProperty
方法:检测指定对象自身上是否有对应的属性值。二者的区别在于in
会查找原型链,而hasOwnProperty
不会。
示例中对象foo
自身上存在goo
属性,而它的原型链上存在bar
属性。
经过这个例子要注意若是要判断foo
上是否有属性goo
,不能简单的经过if(foo.goo){}
判断,由于goo
的值可能为undefined
或者其余可能隐式转换为false的值。
// 输出 'moo' 'bar'
for...in...
遍历对象上除了Symbol
之外的可枚举属性,包括原型链上的属性。
// 输出 { bar: "hello" } undefined
两个函数惟一区别就是return
后面跟的值,一个换行一个不换行。
当咱们书写代码时忘记在结尾书写;
时,JavaScript
解析器会根据必定规则自动补上;
。
return { bar: "hello" } => 会被解析成 return; { bar: "hello" };
所以函数执行后会返回undefined
。
// 输出 'object'
arguments
为类数组,类型为object
。所以typeof arguments = 'object'
。
//输出 false true false fasle
只有下面几种值在转换为布尔值时为false
:
+0,-0,NaN,false,'',null,undefined。
除此以外的值在转换为布尔值的时候所有为true
。
// 输出 [empty × 3] [2,3]
使用Array()
建立数组时,要注意传入的值的类型和数量。
// 输出 false
//输出 1,2,3
join
方法若是省略参数,默认以,
分隔。
// 输出 2
在执行a - b
时,a
和b
都要转换为数字。首先a
先转换为字符串,[3] => [3].toString() => '3'
,而后Number(3) => 3
。b
同理。所以转换以后为3 - 1 = 3
。
若是文中有错误,请务必留言指正,万分感谢。
点个赞哦,让咱们共同窗习,共同进步。