javaScript

152 阅读5分钟

1、数据类型(usbno)

  • 值类型(基本类型)
    • Undefined(未定义)
    • String(字符串)
    • Boolean(布尔)
    • Number(数字)
    • Null(空)
  • 引用数据类型
    • Object(对象)

2、检测一个变量是不是NaN

  • ES6:isNaN()
  • ES5: a != a ,返回false不是NaN,返回true 是NaN

3、IFEE (匿名函数自执行)

function show(){
    console.log('我是正常函数执行')
}
show();

(function () {
    console.log('我是匿名函数自执行')
})();

  • 作用:处理变量污染(防止变量覆盖)
var a = 12
var a = 13
console.log(a) //=> 13

4、closure(闭包)

什么是闭包?

闭包就是指有权访问另一个函数作用域中的变量的函数。

讲解闭包

通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。

function fn1() {
	var name = 'iceman';
	function fn2() {
		console.log(name); fn2的词法作用域能访问fn1的作用域
	}
	return fn2;
}
var fn3 = fn1();
fn3();

这样就清晰地展示了闭包:

  • fn2的词法作用域能访问fn1的作用域
  • 将fn2当做一个值返回
  • fn1执行后,将fn2的引用赋值给fn3
  • 执行fn3,输出了变量name

我们知道通过引用的关系,fn3就是fn2函数本身。执行fn3能正常输出name,这不就是fn2能记住并访问它所在的词法作用域,而且fn2函数的运行还是在当前词法作用域之外了。

正常来说,当fn1函数执行完毕之后,其作用域是会被销毁的,然后垃圾回收器会释放那段内存空间。而闭包却很神奇的将fn1的作用域存活了下来,fn2依然持有该作用域的引用,这个引用就是闭包。

再次解释闭包

闭包绝不仅仅是一个无用的概念,你写过的代码当中肯定有闭包的身影,比如类似如下的代码:

function waitSomeTime(msg, time) {
	setTimeout(function () {
		console.log(msg)
	}, time);
}
waitSomeTime('hello', 1000);

定时器中有一个匿名函数,该匿名函数就有涵盖waitSomeTime函数作用域的闭包,因此当1秒之后,该匿名函数能输出msg。

另一个很经典的例子就是for循环中使用定时器延迟打印的问题:

for (var i = 1; i <= 10; i++) {
	setTimeout(function () {
		console.log(i);
	}, 1000);
}

在这段代码中,我们对其的预期是输出1~10,但却输出10次11。这是因为setTimeout中的匿名函数执行的时候,for循环都已经结束了,for循环结束的条件是i大于10,所以当然是输出10次11咯。

究其原因:i是声明在全局作用中的,定时器中的匿名函数也是执行在全局作用域中,那当然是每次都输出11了。

原因知道了,解决起来就简单了,我们可以让i在每次迭代的时候,都产生一个私有的作用域,在这个私有的作用域中保存当前i的值。

for (var i = 1; i <= 10; i++) {
	(function () {
		var j = i;
		setTimeout(function () {
			console.log(j);
		}, 1000);
	})();
}

这样就达到我们的预期了呀,让我们用一种比较优雅的写法改造一些,将每次迭代的i作为实参传递给自执行函数,自执行函数中用变量去接收:

for (var i = 1; i <= 10; i++) {
	(function (j) {
		setTimeout(function () {
			console.log(j);
		}, 1000);
	})(i);
}

更优雅的写法

for (let i = 1; i <= 10; i++) {
	setTimeout(function () {
		console.log(i);
	}, 1000);
}

5、DOM事件流

什么是事件流

事件流描述的是从页面中接受事件的顺序。 但IE和Netscape开发团队居然提出了两个截然相反的事件流概念。

  • IE的事件流是 事件冒泡流,
  • 高级的浏览器 冒泡和捕获都有, 默认是冒泡,怎么变成捕获?在事件绑定的第三个参数 传个 true,如果不传,默认就是false
<div id="outer">outer
        <div id="box">box
            <div id="myDiv">myDiv</div>
        </div>
</div>
<script>
var outer = document.getElementById('outer');
var box = document.getElementById('box');
var myDiv = document.getElementById('myDiv');

outer.addEventListener('click', function(e) {
    console.log('1. outer 冒泡');
    e = e || window.event; // 后一种为兼容IE的写法
    var target = e.target || e.srcElement;
    console.log(target); // 返回目标元素(事件源)
}, false); 

box.addEventListener('click',function(e) {
    console.log('2. box 冒泡');
    e = e || window.event;
    var target = e.target || e.srcElement; // 后一种为兼容IE的写法
    console.log(target);
}, false);

myDiv.addEventListener('click', function(e) {
    console.log('3. myDiv 冒泡');
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log(target);
}, false);
</script>

事件捕获

1>2>3>4>5

事件冒泡

6>7>8>9>10

6、this的常见面试的坑和解决

  • 全局 this 是 Window

  • 函数 this 是 window, 如果在use strict this 是undefined

  • 对象

    • 严格模式

      'use strict'
      var teacer = {
      	name: 'hell',
      	showName: function(){
      		console.log(this)  // 对象本身 teacer
      	}
      }
      teacer.showName()
      
      'use strict'
      var teacer2 = {
      	name: 'hell',
      	showName: function(){
      		function testThis (){
      			console.log(this)  // undefined
      		}
      		testThis();
      	}
      }
      teacer2.showName()
      
    • 非严格模式

      var name = '逍遥'
      var teacer2 = {
      	name: 'hell',
      	showName: function(){
      	    console.log(this)  // 对象本身 teacer2
      		function testThis (){
      		    console.log(this)  // Window
      			console.log(this.name)  // 逍遥
      		}
      		testThis();
      	}
      }
      teacer2.showName()
      

7、call、apply、bind

  • 意义: 改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。
  • call、apply与bind的差别
    • call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数,并不执行。
  • call、apply的区别
    • call和aplly的第一个参数都是要改变上下文的对象,
    • call从第二个参数开始以参数列表的形式展现,fn.call(obj, 10, 20, 30)
    • apply从第二个参数开始放在一个数组里面作为它的第二个参数fn.apply(obj, [10, 20, 30])。
  • 那个性能更好
    • call的性能要比apply好,尤其是传递给函数参数超过三个的时候
function show(){
	console.log(this) // window
}
show();
function show(){
	console.log(this) // '我是不是你最疼爱的人'
}
show.call('我是不是你最疼爱的人');

8、创建对象的几种方法及继承

  1. 单体模式
    var tracher = {
    	name: '李瑶',
    	age:18,
    	showName: function(){
    		return this.name
    	}
    };
    tracher.showName();
    
  2. 原型模式
    function tracher(name, age){
    	this.name = name;
    	this.age = age;
    }
    
    tracher.prototype.showName = function(){
    	return this.name;
    };
    
    var dabinge = new tracher('liyao', 18);
    
    dabinge.showName();
    
  3. 伪类模式
    class tracher{
    	constructor(name, age){
    		this.name = name;
    		this.age = age;
    	}
    	showName(){
    		return this.name
    	}
    }
    
    var debinge = new tracher('liyao', 18);
    debinge.showName();
    

9、面向对象继承