【前端必备】三、JS篇

1.运算符与隐式类型转换

类型判断:

typeof constructor.toString().indexOf()
NaN是number
Array、Date、Null都是Object
function是function 未定义变量都是undefined
"John".constructor // 返回函数 String()
(3.14).constructor// 返回函数 Number() 
false.constructor// 返回函数 Boolean()
[1,2,3,4].constructor// 返回函数Array()
{name:'John', age:34}.constructor  // 返回函数 Object() 
new Date().constructor// 返回函数 Date()
function () {}.constructor// 返回函数 Function()

类型转换:

  • 对象转基本类型:
    先调用valueOf()再调用toString()
  • 其他简单类型-->String:
方法 注意
变量+""
String(变量)
变量.toString() ull和undefined这两个值没有toString()方法,
Number.toString(2);将数字转成二进制
ArrayObject.toString()会转为逗号分隔的列表内容
  • 其他数据类型-->Number:
方法 注意
Number(变量) true-->1 ; false-->0 ; null-->0 ;""或空格串-->0;
undefined-->NaN;非纯数字串-->NaN;
parseInt(变量,进制) 只保留开头数字,小数自动截断,非字符串会先自动转字符串。
两个参数时做进制转换
parseFloat(变量) 可以获得有效的小数部分
  • 转换为Boolean:
    0、-0、NaN、空串、null、undefined会转成false,其他都转成true
  • 伪数组转数组:
    Array.prototype.slice.call(arguments)

隐式类型转换:

算术运算符
- * / +
自动转换为Number
任何值和NaN计算结果都是NaN
任何的值和字符串做加法运算都会先转换为字符串,然后再做拼串操作
如[1,2]+[2,3]="1,22,3" 'a'+ +'b'="aNaN"
一元运算符
typeof + - ++ --
对一个其他的数据类型使用+ - ++ -- 会将其转换为数字
逻辑运算符
&& || !
能参与逻辑运算的都是布尔值
非布尔值会先转换成布尔值
关系运算符
&& || !
*比较:非数值比较会先转换成数字
    两个字符串比较不会转换,而是比较它们的unicode编码
    任何值与NaN作比较都是false。(只有NaN自己不等于自己)
*判等:==不同类型会转为相同类型(大部分时候转为数字)
    ===不会做类型转换

调用非函数,或者读取null或者undefined的属性时,会报错 :

isNaN("foo"); // true  
isNaN(undefined); // true    
isNaN({}); // true

2.创建对象的方法

  1. new Object();
  2. 工厂函数(一个返回Object的函数)
  3. 构造函数,必须使用new来调用,this指向新建的对象。
    new 一个构造函数的执行流程
    (1)开辟内存空间,存储新创建的对象
    (2)链接到原型,
    (3)绑定this到新建对象,执行构造函数
    (4)将新建的对象作为返回值返回

通过同一个构造函数实例化的多个对象具有相同的原型对象。
实例对象有一个__proto__属性,指向该实例对象对应的原型对象
实例.__proto__.constructor==构造函数 //true
构造函数有prototype属性,实例有__proto__属性
实例.__proto__==构造函数.prototype

试着实现一下instanceof:

function instanceof(left, right) {  
    // 获得类型的原型  
    let prototype = right.prototype  
    // 获得对象的原型  
    left = left.__proto__  
    // 判断对象的类型是否等于类型的原型  
    while (true) {  
    	if (left === null)  
    		return false  
    	if (prototype === left)  
    		return true  
    	left = left.__proto__  
    }  
}

3.闭包

闭包是指有权访问另一函数作用域中的变量的函数。
换句话说,在函数内定义一个嵌套的函数时,就构成了一个闭包, 它允许嵌套函数访问外层函数的变量。 通过返回嵌套函数,允许你维护对外部函数中局部变量、参数、和内函数声明的访问。
这种封装允许你在外部作用域中隐藏和保护执行环境,并且暴露公共接口,进而通过公共接口执行进一步的操作。

缺点:函数执行完后, 函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄露。
解决:能不用闭包就不用,及时释放。(将引用置为null)
经典面试题:
循环中使用闭包解决 var 定义函数的问题:

  • 解决方法一,使用闭包:
    (包装了一层立即执行函数,这样就不会污染全局变量了)

  • 解决方法二,使用let:

  • 解决方法三,使用setTimeout的第三个参数:

4.作用域和上下文

作用域(Scope)和上下文(Context)
作用域决定了代码区块中变量和其他资源的可见性。它指一个变量的作用范围。相对于上下文对象是静态的, 在编写代码时就确定了。

上下文(context)是指 this 在同一作用域内的值,是代码的执行环境。
执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放。
有三种类型的ECMAScript代码:全局代码,函数代码和eval代码。代码执行在它的执行上下文里。有唯一的全局上下文,以及可能有多个函数和eval上下文。

1.在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2.在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3.在函数执行上下文创建后, 将其添加到栈中(压栈)
4.在当前函数执行完后,将栈顶的对象移除(出栈)
5.当所有的代码执行完后, 栈中只剩下window

5.内存溢出和内存泄漏

内存溢出:一种程序运行出现的错误。当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误。
内存泄漏:占用的内存没有及时释放。
注意,内存泄露的次数积累多了,就容易导致内存溢出。
常见的内存泄露:
•意外的全局变量
•没有及时清理的计时器或回调函数

6.深浅拷贝

将引用复制改为值复制:

  • Object.assign(target, ...sources)
    将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。 不会跳过那些值为 null 或 undefined 的源对象。
    只能解决第一层的问题
  • 展开运算符 let b = {...a}
    只能解决一层
  • JSON.parse(JSON.stringify(object))
    会忽略 undefined、symbol
    不能序列化函数
    不能解决循环引用(父子循环引用、兄弟循环引用)的对象
/**
* 对象克隆
* 支持基本数据类型及对象
* 递归方法
*/
// 方法一:
function clone(obj) {
	var o;
	switch (typeof obj) {
		case "undefined":
			break;
		case "string":
		o = obj + "";
		break;
		case "number":
			o = obj - 0;
			break;
		case "boolean":
			o = obj;
			break;
		case "object": // object 分为两种情况 对象(Object)或数组(Array)
		if (obj === null) {
			o = null;
		} else {
			if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
			o = [];
			for (var i = 0; i < obj.length; i++) {
				o.push(clone(obj[i]));
				}
			} else {
			o = {};
			for (var k in obj) {
				o[k] = clone(obj[k]);
				}
			}
		}
			break;
		default:
		o = obj;
		break;
	}
	return o;                                 
} 

7.异步与同步

  js 是单线程,而且有一个任务队列:全部的同步任务执行完毕后,再来执行异步任务。
  同步任务在主线程上排队执行,异步任务在任务队列等待,主线程上的同步任务执行完了,才会从任务队列取出异步任务执行。
常见的异步操作:网络请求、定时任务、事件绑定、promise

异步编程的4种方式:
  • 回调函数
  • 事件监听
f1.on('done', f2);
f1.trigger('done');
  • 发布/订阅
jQuery.subscribe("done", f2);
jQuery.publish("done");
jQuery.unsubscribe("done", f2);
  • promise对象

8.伪数组

特性:具有length属性、按索引方式存储数据、不具有数组的push,pop等方法。
例子:函数的arguments参数,调用getElementsByTagName,document.childNodes之类的返回的NodeList对象。
将伪数组转为数组:

function log(){ 
	var args = Array.prototype.slice.call(arguments); 
	//为了使用unshift数组方法,将argument转化为真正的数组 
	args.unshift('(app)'); 
	console.log.apply(console, args);
 };

9.arguments的callee和caller属性

两个属性都是指针
arguments.callee指向当前函数,
arguments.caller指向当前函数的父函数
可用于递归函数中的自调用,实现函数执行与函数名的解耦合。

10.this、call、apply、bind

this 表示当前对象的一个引用
• 在方法中,this 表示该方法所属的对象。
• 如果单独使用,this 表示全局对象。
• 在函数中,this 表示全局对象。
• 在函数中,在严格模式下,this 是未定义的(undefined)。
• 在事件中,this 表示接收事件的元素。
类似 call() 和 apply() 方法可以将 this 引用到任何对象。

call、apply是立即调用函数,apply的第二个参数是参数数组。

persion1.say.call(p2, "实验小学", "六年级");  
persion1.say.apply(p2, ["实验小学", "六年级"]); 

将第一个参数p2作为say方法内this的指向,后面的参数作为say函数的参数。
bind传参与call相同,但bind返回一个函数,要加()才能立即执行。
persion1.say.bind(p2, "实验小学", "六年级")();

11.事件机制

事件触发的三个阶段 :

  路径:Window <——> Document <——> <html> <——> … <——> 事件触发处

  • 捕获阶段:遇到注册的捕获事件会触发
  • 目标阶段:传播到事件触发处时触发注册的事件
  • 冒泡阶段:遇到注册的冒泡事件会触发
    如果给一个目标节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行。

注册事件:

  属性注册:
  

  • <button onlick="sayHello()">点击</button>

  方法注册:

  • element.addEventListener(event, function, useCapture);
        //usecapture默认false冒泡,true捕获
  • element.attachEvent(event,function); //IE中
  • EventTarget.onEventName=function(e){};

注销事件:

  • removeEventListener(event,function,capture/bubble); 
  • detachEvent(event,function); 
  • 事件属性赋值为null

阻止事件冒泡:

box3.onclick = function (event) {
alert("child");
//阻止冒泡
    event = event || window.event;
if (event && event.stopPropagation) {
        event.stopPropagation();//w3c(火狐、谷歌、IE11)
    } else {
        event.cancelBubble = true;//IE10以下
    }
}

事件委托(事件代理):

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该委托给父节点
优点:减少内存消耗,提高性能。

12.BOM与DOM

BOM与DOM的关系:

系统对话框:

alert();	//不同浏览器中的外观是不一样的
confirm();  //兼容不好
prompt();	//不推荐使用

window操作窗口

  window.open(url,target,param)
  window.close()
  新窗口.moveTo(5,5)
  新窗口.resizeTo()
  window.resizeBy()

location对象:

window.location可以简写成location。location相当于浏览器地址栏,可以将url解析成独立的片段。

  • 属性
    • href:跳转
    • hash 返回url中#后面的内容,包含#
    • host 主机名,包括端口
    • hostname 主机名
    • pathname url中的路径部分
    • protocol 协议 一般是http、https
    • search 查询字符串
  • 方法
    • location.assign():改变浏览器地址栏的地址,并记录到历史中
      设置location.href 就会调用assign()。一般使用location.href 进行页面之间的跳转。
    • location.replace():替换浏览器地址栏的地址,不会记录到历史中
    • location.reload():重新加载

window.navigator的一些属性 可以获取客户端的一些信息。
appCodeName appName appVersion cookieEnabled platform userAgent systemLanguage

history对象:

window.history 包含用户(在浏览器窗口中)访问过的 URL。

方法 描述
back() 加载 history 列表中的前一个 URL
forward() 加载 history 列表中的下一个 URL
go() 加载 history 列表中的某个具体页面

screen对象:

Screen 对象包含有关客户端显示屏幕的信息。
availHeight availWidth colorDepth height width pixelDepth

posted @ 2019-03-27 15:58  鱼桑燕子梁  阅读(363)  评论(0编辑  收藏  举报