JS
1、原型/原型链/构造函数/实例/继承
1. proto(原型)
每个对象又有proto属性,指向创建他的构造函数的原型对象(实例指向原型对象的指针)
2.prototype原型对象
每个函数都有一个prototype属性,是指向一个对象的引用,这个对象成为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作new时,新创建的对象会从原型上继承属性和方法
3.原型链
原型可以通过proto访问到原型的原型,比如构造函数Person继承前者的有一个构造函数People,然后new People得到实例p
image
4.构造函数Constructor 实例、
new运算符创建的函数,其实就是构造函数,构造函数创建出的对象,就是实例
5.继承
子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程
2、有几种方式可以实现继承
6种方式实现继承
想要继承,必须有一个父类
function Person(name){
this.name=name
this.sum = function(){
alert(this.name)
}
}
Person.prototype.age = 10;
- 原型链继承
function Per(){
this.name = "ker"
}
Per.prototype = new Person();//原型链继承
var per1 = new Per();
console.log(per1.age)//10
// instanceof 判断元素是否早另一个元素的原型链上
// per1 继承了Person的属性,返回true
console.log(per1 instanceof Person)
重点:需要让新实例的原型,被父类的实例赋值
优点:实例可继承的属性有: 实例的构造函数的属性,父类构造函数属性,父类原型的属性
缺点:1. 新实例无法向父类构造函数传参
2. 继承单一
3. 所有新实例都会共享父类实例属性,原型上的属性是共享的,一个实例修改了原型属性,所有实例的原型属性也会被修改
- 构造函数继承
function Con(){
Person.call(this,"jer")//重点
this.age = 12;
}
var con1 = new Con();
console.log(con1.name);//"jer"
console.log(con1.age);//12
console.log(con1 instanceof Person)//false
重点:用call()和apply()将父类构造函数引入子类函数
优点: 1. 只继承了父类构造函数的属性,没有继承父类原型属性
2. 解决了原型链继承缺点123
3. 可以继承多个构造函数属性(call多个)
4. 在子实例中可以向父实例传参
缺点: 1. 只能继承附列构造函数属性
2. 无法实现构造函数的复用(每次用每次都要重新调用)
3. 每个新实例都有父类构造函数的副本,臃肿
- 组合继承(组合原型链继承和构造函数继承)(常用)
function SubType(name){
Person.call(this,name)//借用构造函数继承
}
SubType.prototype = new Person()//原型链继承
var sub = new SubType("gar")
console.log(sub.name)//“gar”继承了构造函数属性
console.log(sub.age);//10 继承了父类原型的属性
重点: 结合了两种模式的有点,传参和复用
优点: 1. 可以继承父类原型上的属性,可以传参,可以复用
2. 每个新实例引入的构造函数是私有的
缺点: 调用两次父类的构造函数,子类的构造函数会代替原型上那个父类构造函数
- 原型式继承
// 先封装一个函数容器,用来输出对象和承载继承的原型
function content(obj){
function F(){}
F.prototype = obj;//继承了传入的参数
return F(); //返回函数对象
}
var sup = new Person();// 拿到父类实例
var sup1 = content(sup);
console.log(sup1.age);// 10 继承了父类函数的属性
重点: 用函数包裹对象,然后返回函数的调用,这个函数就变成了可以随意添加属性的实例或对象,object.create()就是这个原理
特点: 类似于复制一个对象,用函数来包装
缺点: 1. 所有实例都会继承原型上属性
2. 无法实现复用,新实例属性都是后面添加的
- 寄生式继承
function content(obj){
function F(){}
F.prototype = obj
return new F();
}
var sup = new Person();
function subobject(obj){
var sub = content(obj)
sub.name = "gar"
return sub;
}
var sup2 = subobject(sup)
console.log(typeof subobject)//function
console.log(typeof sup2)//object
console.log(sup2.name)//"gar" 返回了个sub对象,继承了sub的属性
重点:就是给原型式继承外面套了壳子
特点: 没有创建自定义类型,因为只是套了壳子返回对象,这个函数也就成为了新对象
缺点:没用到原型,无法复用
- 寄生组合式继承(常用)
3、用原型实现继承有什么缺点,怎么解决
缺点
-
重写子类的原型 等于 父类的一个实例,(父类的实例属相变成子类的原型属性)如果父类包含引用类型的属性,那么子类所有实例都会共享该属性
-
在创建子类实例时,不能向父类的构造函数传递参数
解决办法:组合继承解决原型链继承的引用类型原型属性被实例共享问题
4、arguments
它是js的一个内置对象,常被忽略,js不像JAVA是显示传递参数,js传的是形参,可以传也可以不传,若方法里面没有写参数,却传入了参数,可用arguments拿到参数
每一个函数都有一个arguments对象,他包括了函数所要调的参数,通常我们把它当作数组使用,用它的length得到参数数量,但它是类数组对象,无法使用push
5、数据类型判断
- typeof
typeof '';//string 有效
typeof 1; // number 有效
typeof Symbol();//symbol 有效
typeof true; // boolean
typeof undefined; //undefined 有效
typeof null; // object 无效
typeof []; // object 无效
typeof new Function(); // functionyoux
typeof new Date(); // object 无效
typeof new RegExp; //object 无效
-
instanceof
用于判断A是否是B的实例,表达式为 A instanceof B -
constructor
console.log('数据类型判断' - constructor);
console.log(arr.constructor === Array);//true
console.log(date.constructor === Date); // true
console.log(fn.constructor === Function); // true
- toString
Object.prototype.toString.call('');//[object String]
Object.prototype.toString.call(1); //[object Nmuber]
Object.prototype.toString.call(document)//[object HTMLDocument]
Object.prototype.toString.call(window)//[object global]
6、作用域链、闭包、作用域
作用域
js的作用域是靠函数形成的,函数外不可访问函数内的变量,作用域就是变量和函数的可访问的范围。
作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)来保证对执行环境有权访问的变量和函数的有序访问
闭包
能够访问另一个函数作用域中变量的函数
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner //inner就是一个闭包函数,因为他可以访问outer函数的作用域
}
闭包的问题
- 引用的变量可能发生变化
- this指向
- 内存泄漏
闭包的作用
- 解决递归调用
- 模仿块级作用域
7、Ajax的原生写法
function ajax(opts){
var xhr = new XMLHttpRequest();//创建ajax对象
xhr.onreadystatechange = fucntion(){
if(xhr.readyState ===4 & xhr.status===200){
opts.success(JSON.parse(xhr.responseText))
}else if(xhr.readyState === 4 & xhr.status !== 200){
opts.error();
}
}
var urlStr = ''
for (var key in opts.data){
urlStr = key + '=' + opts.data[key] + '&';
}
urlStr = urlStr.substing(0, urlStr.length-1);
if(opts.type.toLowerCase() === 'get'){
xhr.open(opts.type, opts.url + '?' + urlStr, true)
xhr.send()
}
if(opts.type.toLowerCase() === 'post'){
xhr.open('post', opts.url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(urlStr);
}
};
btn.addEventListener('click', function(){
ajax({
url: 'list.json',
type: 'post',
success: fucntion(data){
console.log(data.list)
},
error: function(){
console.log('发生了错误...')
}
})
})
8、对象深拷贝、浅拷贝
对象是引用类型,所以直接赋值修改赋值后的数值,原对象会改变
var boy = {
age: 18
}
var girl = boy;
console.log(boy === girl) //true
girl.age = 20
console.log(boy.age);//20
浅拷贝
var boy = {
age: 18,
address: {
home: '天堂'
}
}
var girl = Object.assign({},boy);
console.log(boy === girl) //false
girl.age = 20;
console.log(boy.age);//18
girl.address.home = '上海'
console.log(boy.address.home);//上海
因为Object.assign()只是浅拷贝girl.address是对栈对象的引用,因此内层对象的修改会影响原始对象。
深拷贝
- JSON.parse()和JSON.stringify()深拷贝(仅适用纯JSON对象)
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
- 对象遍历
//包含其他负责的内容 date对象 null undefined
var obj1={
name:"张三",
age:20,
height:[12,26,46],
address:{
home:'北京'
},
birthday:new Date(),
father:null,
mother:undefined,
school:[
{
middleschool:'北大附中',
},
{
university:'清华大学',
}
]
}
function clone(obj) {
if(obj === null) return null
if(typeof obj !== 'object') return obj;
if(obj.constructor === Date) return new Date(obj);
if(obj.constructor === RegExp) return new RegExp(obj);
var newObj = new obj.constructor (); //保持继承链
for (var key in obj) {
if (obj.hasOwnProperty(key)) { //不遍历其原型链上的属性
var val = obj[key];
newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
}
}
return newObj;
};
console.log(obj1);
console.log(clone(obj1));
-
用 new obj.constructor ()构造函数新建一个空的对象,可以保持原形链的继承;
-
用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。
-
函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee。
9、图片懒加载、预加载
图片预加载
即提前加载图片,可保证图片快速、无缝地发布,用户需要查看时可直接从本地缓存中渲染,适用于图片占据很大比例的网站。
- js new image对象 设置src加载
function preloader(){
if (document.images) {
var img1 = new Image()
var img2 = new Image()
var img3 = new Image()
img1.src = "";
img2.src = "";
img3.src = "";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'fucntion') {
window.onload = func;
}else {
window.onload = function (){
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
div.appendChild(img1)//插入到DOM
- Ajax预加载, new Image()对象设置src
window.onload = function() {
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.send();
xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.send()
// preload image
new Image().src = '';
}, 1000);
}
懒加载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload 2</title>
<style>
img {
display: block;
margin-bottom: 50px;
height: 200px;
}
</style>
</head>
<body>
<img src="images/loading.gif" data-src="images/1.png">
<img src="images/loading.gif" data-src="images/2.png">
<img src="images/loading.gif" data-src="images/3.png">
<img src="images/loading.gif" data-src="images/4.png">
<img src="images/loading.gif" data-src="images/5.png">
<img src="images/loading.gif" data-src="images/6.png">
<img src="images/loading.gif" data-src="images/7.png">
<img src="images/loading.gif" data-src="images/8.png">
<img src="images/loading.gif" data-src="images/9.png">
<img src="images/loading.gif" data-src="images/10.png">
<img src="images/loading.gif" data-src="images/11.png">
<img src="images/loading.gif" data-src="images/12.png">
<script>
function throttle(fn, delay, atleast) {//函数绑定在 scroll 事件上,当页面滚动时,避免函数被高频触发,
var timeout = null,//进行去抖处理
startTime = new Date();
return function() {
var curTime = new Date();
clearTimeout(timeout);
if(curTime - startTime >= atleast) {
fn();
startTime = curTime;
}else {
timeout = setTimeout(fn, delay);
}
}
}
function lazyload() {
var images = document.getElementsByTagName('img');
var len = images.length;
var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
return function() {
var seeHeight = document.documentElement.clientHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(var i = n; i < len; i++) {
if(images[i].offsetTop < seeHeight + scrollTop) {
if(images[i].getAttribute('src') === 'images/loading.gif') {
images[i].src = images[i].getAttribute('data-src');
}
n = n + 1;
}
}
}
}
var loadImages = lazyload();
loadImages(); //初始化首页的页面图片
window.addEventListener('scroll', throttle(loadImages, 500, 1000), false);
//函数节流(throttle)与函数去抖(debounce)处理,
//500ms 的延迟,和 1000ms 的间隔,当超过 1000ms 未触发该函数,则立即执行该函数,不然则延迟 500ms 执行该函数
</script>
</body>
</html>
10、实现页面加载进度条
11、this关键字
12、函数式编程
13、手动实现parseInt
function _parseInt (string, radix) {
if (typeof string !== "string" && typeof string !== "number") return NaN;
if (radix && (typeof radix !== "number" || /^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/.test(radix) || radix > 36 || radix < 2)) return NaN;
string = String(string)
var rexp = (radix == 10) ? /(-?)([0]?)([0-9]+)/ : /(-?)([0]?[Xx]?)([0-9a-fA-F]+)/,
a = string.match(rexp),
sign = a[1],
rawRadix = a[2],
rawNum = a[3],
result = 0,
strArr = rawNum.split(''),
len = strArr.length,
numArr = [];
if (a && !radix) {
if ( rawRadix.toUpperCase() === "0X") {
radix = 16;
} else if (rawRadix === "0") {
radix = 8;
} else {
radix = 10;
}
}
for (var i = 0; i < len; i++){
var num;
var charCode = strArr[i].toUpperCase().charCodeAt(0);
if(radix <=36 && radix >= 11) {
if (charCode >= 65 && charCode <= 90) {
num = charCode - 55;
} else {
num = charCode - 48;
}
} else {
num = charCode - 48;
}
if (num < radix) {
numArr.push(num);
} else {
return NaN
};
}
if(numArr.length > 0) {
numArr.forEach(function(item, j){
result += item * Math.pow(radix, numArr.length-j-1);
})
}
if(sign === "-"){
result = -result;
}
return result
}
// 以下例子均返回15:
console.log(_parseInt("F", 16));
console.log(_parseInt("17", 8));
console.log(_parseInt("15", 10));
console.log(_parseInt(15.99, 10));
console.log(_parseInt("FXX123", 16));
console.log(_parseInt("1111", 2));
console.log(_parseInt("15*3", 10));
console.log(_parseInt("12", 13));
// 以下例子均返回 NaN:
console.log(_parseInt("Hello", 8)); // Not a number at all
console.log(_parseInt("546", 2)); // Digits are not valid for binary representations
// 以下例子均返回 -15:
console.log(_parseInt("-F", 16));
console.log(_parseInt("-0F", 16));
console.log(_parseInt("-0XF", 16));
console.log(_parseInt(-15.1, 10));
console.log(_parseInt(" -17", 8));
console.log(_parseInt(" -15", 10));
console.log(_parseInt("-1111", 2));
console.log(_parseInt("-15e1", 10));
console.log(_parseInt("-12", 13));
// 下例中也全部返回 17,因为输入的 string 参数以 "0x" 开头时作为十六进制数字解释,而第二个参数假如经过 Number 函数转换后为 0 或 NaN,则将会忽略。
console.log(_parseInt("0x11", 16));
console.log(_parseInt("0x11", 0));
console.log(_parseInt("0x11"));
// 下面的例子返回 224
console.log(_parseInt("0e0",16));
14、为什么会有同源策略
- 协议、域名、端口号一致,表示同源
- 不能通过ajax请求不同源的数据,不能通过脚本操作不同域下的DOM
- 设置同源限制主要是为了安全,如果没有同源限制存在浏览器中的cookie等其他数据可以任意读取,不同域下DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据
15、怎么判断两个对象是否相等
// 传入两个对象
function isObjectValueEqual(a, b) {
// Object.getOwnPropertyNames()
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
if (a[propName] !== b[propName]) {
return false;
}
}
return true;
}
16、事件模型
事件与事件流
事件是浏览器与文档交互的瞬间,比如点击按钮,填写表格等操作,它是Javascript与HTML之间沟通的桥梁,DOM是树状结构,如果同时给父节点绑定时间,触发子节点时,两个事件发生的顺序就牵涉到事件流的内容,它描述的是页面接受事件的顺序,事件流描述的是从页面接受事件的顺序,但是IE的事件流食冒泡流,而Netspace Communicator的事件流是捕获流
- IE的事件流叫做事件冒泡。即事件开始由最具体的元素接收,然后逐级向上传播到不具体的节点。事件捕获则相反,是不太具体的节点硬更早接收事件,具体的节点应该最后接收事件。
- DOM0级事件模型
是早期的事件模型,又称为原始事件模型,在该模型中,事件不会传播,即没有事件流的概念。事件绑定监听函数较为简单,要使用js指定事件处理程序,首先取得一个要操作的对象的引用。
每个元素都有自己的事件处理属性,通常全部小写,例如onclick,设置为函数即可指定事件处理程序:
btn = document.getElementById("myBtn" = "Clicked!")
// HTML事件处理程序
<form method = "post">
<input type="text" name="username" value="">
<input type="button" value="Username" onclick="alert(username.value)">
</form>
- DOM2级事件模型
在该模型中,分为三个过程:事件捕获,处于目标阶段,事件冒泡阶段
- 事件捕获阶段。事件从document一直向下传播到目标元素,依次检查经过的节点是否绑定了时间监听函数,如果有则执行
- 事件处理阶段。事件达到目标元素,触发目标元素的监听函数
- 事件冒泡阶段。事件从目标元素冒泡到document,一次检查经过节点是否绑定了事件监听函数,如果有则执行,定义了两个方法addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,并且有三个参数,要处理的事件名、作为事件处理程序的函数和一个布尔值。要在click事件添加事件处理程序,可以用:
var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
}, false);
btn.addEventListener("click",function(){
alert("Hello kid")
},false)
执行顺序为"myBtn","Hello kid"。IE中执行顺序相反
移除事件监听方式如下
btn.removeEventListener("click")
- IE中的事件模型
var btn = document.getElementById("myBtn");var handler = function() {
alert(this.id);
};
btn.attachEvent("onclick", handler);//添加事件处理程序btn.detachEvent("onclick", handler);//删除事件处理程序
事件委托、代理
利用事件冒泡的原理,把目标事件委托给父元素
优点:
- 管理函数变少了,不需要为每个元素都添加监听函数,对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理
- 可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。(比如后来添加子元素依然拥有事件)
- JavaScript和DOM节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。
应用场景
很多商品放在一个ul下面的li标签里面,点击添加或删除商品,就可以绑定商品的父元素ul标签,通过事件代理去找到要点击的商品,完成添加删除事件
如何让事件先冒泡后捕获
17、window的onload事件和domcontentloaded
- 当onload触发时,页面上所有的DOM,CSS,script,图片,flash等全部加载完成了
- DOMContentLoaded事件触发时,仅仅DOM加载完成,其余均未完成
18、for...in迭代和for...of有什么区别
for in
// for in 应用于数组
Array.prototype.sayHello = function(){
console.log("Hello")
}
Array.prototype.str = "world";
var myArray = [1,2,10,30,100];
myArray.name = "数组"
for(let index in myArray) {
console.log(index);
}
// 结果: "0" "1" "2" "3" "4" "name" "sayHello" "str"
// for in 应用于对象中
Object.prototype.sayHello = function(){
console.log('Hello');
}
Object.prototype.str = "World";
var myObject = {name: 'hanlei', age:100};
for(let index in myObject){
console.log(index);
}
// 输出结果:"name" "age" "sayHello" "str"
// 首先输出的是对象的属性名,再是对象原型的属性和方法
//如果不想让其输出原型中的属性和方法,可以使用hasOwnProperty方法进行过滤
for (let index in myObject){
if(myObject.hasOwnProperty(index)){
console.log(index)
}
}
// 输出结果为: "name" "age"
// 也可以通过Object.keys()方法获取所有的自身可枚举属性组成的数组
Object.keys(myObject)
经观察,for in应用于数组返回是数组下标和属性和原型上的方法和属性,而对象中返回的是对象的属性名和原型中的方法和属性
for in遍历数组时,会存在几个问题
- index索引为字符串,不能进行运算
- 遍历顺序有可能不是按照数组内部顺序
- for in会遍历所有可枚举属性,包括原型
for of
Object.prototype.sayHello = function(){
console.log('Hello');
}
var myObject = {
name: 'hanlei',
age: 100
}
for(let key of myObject){
console.log(key)
}
// 结果:TypeError
Array.prototype.sayHello = function(){
console.log('hello');
}
var myArray = [1,2 ,1231,1,56877]
for (let key of myArray) {
console.log(key);
}
for of遍历的是数组的元素值
总结:
- 遍历对象用for in,遍历数组使用for of
- for in循环出的是key,for of循环出的是value
- 注意 for of是ES6新特性,为了修复ES5for in的不足
- for of 不能遍历普通对象,需要通过Object。keys()搭配使用
19、函数柯里化
柯里化(currying),是把接受多个参数的函数变换成接受一个单一参数的函数,并返回接受剩下参数且返回结果的新函数的技术。
即只传给函数一部分参数调用它,让它返回一个函数,去处理剩下的参数
function add(x, y, z) {
return x + y + z;
}
console.log(add(1, 2, 3))//6
var add = function(x) {
return function(y) {
return funciton (z) {
return x + y + z;
}
}
}
var addOne = add(1);
var addOneAndTwo = addOne(2);
var addOnetoThree = addOneAndTwo(3);
console.log(addOnetoThree)
通过闭包记住第一个参数以此类推,可以用ES6的箭头函数写成这样:
const add = x => y => z => x + y + z
20、call apply区别,原生实现bind
bind,apply,bind方法,其实是函数的另外一种调用方法,功能上等同于<code>fn</code>,只是调用者由bind,apply,call来决定,也就是<code>bind,apply,call</code>指定函数执行上下文
声明一个函数后,基本调用方式为:
// 方法定义
var obj = {
todo: function(){
console.log("rushB");
}
}
// 调用
obj.todo();// "rushB"
// 函数声明
function todo() {
cosole.log("rushA");
}
// 调用
todo();
// 函数表达式
var todo = function() {
console.log("rush不动了")
}
// 调用方法
todo()
接下里用<code>bind,apply,call</code>对上面进行改写
var obj = {
todo: function() {
console.log("rushA");
}
}
// 调用方式
obj.todo.apply();
obj.todo.call();
obj.todo.bind()();
// 函数声明
function todo() {
console.log("rushB");
}
// 调用方式
var todo = function() {
console.log("rush不动了")
}
// 函数表达式
todo.apply();
todo.call();
todo.bind()();
可以传参数,第一个参数就是受益者,this的指向对象,第二个参数是arguments
fn.apply(otherObj);
fn.call(otherObj);
fn.bind(otherObj)()
需要调用this才生效
var csgo = "rushAB";
var obj = {
csgo: "rushA",
fn: function(){
console.log(this.csgo)
}
}
obj.fn()
//this指向window,获得想要的window上的属性
obj.fn.apply(window)
obj.fn.call(window)
obj.fn.bind(window)()
如果将this换成obj
var obj = {
csgo: "rushA",
fn: function(){
console.log(obj.csgo)
}
}
obj.fn()
//this指向window,没有获得window上的属性
obj.fn.apply(window)
obj.fn.call(window)
obj.fn.bind(window)()
三个方法允许我们明确指定方法中的this指向
bind,aplly,call的区别
-
apply 和 call 的用法几乎相同, 唯一的差别在于当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。
-
在上面的代码片段中,我们可以看出bind使用方法和apply, call 不一样,多了一个执行()。那是因为bind在函数调用后,实际上会返回一个新函数。
-
bind,aplly,call中只有apply接收一个数组,可以通过apply和array两个单词尾部都是y记忆。bind,call则都是接收一系列单独变量。
注意
Array.prototype.slice.apply.(obj),可以让一个类数组对象使用数组的方法,类数组主要是指通过DOM 操作获取的DomList 和arguments,都是具有length属性的对象。
Object.prototype.toString.call(obj).slice(8, -1) 方法获取对象属性,比typeof()更可信。
在 ES6 的箭头函数下, call 和 apply 的失效
不可以当作构造函数, 也就是说不可以使用 new 命令, 否则会抛出一个错误
call,apply,bind 三者用法和区别:角度可为参数、绑定规则(显示绑定和强绑定),运行效率、运行情况。
21、async/await
22、立即执行函数和使用场景
23、设计模式(要求说出如何实现,应用,优缺点)/单例模式实现
24、iframe的缺点有哪些
25、数组问题
- 数组去重
- 遍历数组法
实现思路:新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf为-1)就加入该新数组中
var arr=[2,8,5,0,5,2,6,7,2];
function unique1(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(hash.indexOf(arr[i])==-1){
hash.push(arr[i]);
}
}
return hash;
}
- 数组下标判断法
实现思路:如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组。
function unique2(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(arr.indexOf(arr[i])==i){
hash.push(arr[i]);
}
}
return hash;
}
- 排序后相邻去除
实现思路:给传入的数组排序,排序后相同的值会相邻,然后遍历排序后数组时,新数组只加入不与前一值重复的值
function unique3(arr){
arr.sort();
var hash=[arr[0]];
for (var i = 1; i < arr.length; i++) {
if(arr[i]!=hash[hash.length-1]){
hash.push(arr[i]);
}
}
return hash;
}
- 优化遍历数组法
实现思路:双层循环,外循环表示从0到arr.length,内循环表示从i+1到arr.length
将没重复的右边值放入新数组。(检测到有重复值时终止当前循环同时进入外层循环的下一轮判断)
function unique4(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
for (var j = i+1; j < arr.length; j++) {
if(arr[i]===arr[j]){
++i;
}
}
hash.push(arr[i]);
}
return hash;
}
- set
let arr = [4, 1, 3, 3, 2, '2'];
let uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [4, 1, 3, 2, "2"]
数组常用方法
split()//字符串 ->数组转换
join()//数组 ->字符串转换, 转换时可以添加符号, 还有一个是toString()
indexOf()
//查找元素返回第一个与参数相同的元素的索引。有另外函数 lastIndexOf(),
//该函数返回相同元素中最后一个元素的索引,如果没找到相同元素,则返回 -1
push()
pop()
unshift()
shift()
concat()
splice()
reverse()
sort()
forEach()
every()
some()
reduce(function (runningTotal, currentValue) {
return runningTotal + currentValue;
})
//reduceRight()
map()
filter()
- 查找数组重复项
- 利用sort方法,先使用sort方法将数组排序,再来判断找出重复元素
// arrayObject.sort(sortby) 可选参数sortby,必须是函数
function res(arr){
var temp = [];
// 排序之后 相邻对比
arr.sort().sort(function(a,b){
if(a === b&temp.indexOf(a)===-1){
temp.push(a)
}
})
return temp;
}
var arr = [1,2,3,3,3,3,4,4,5,6,7,7,9,5,4,3,7,7]
console.log(arr.sort())
console.log(res(arr));
- 扁平化数组
- 按数组中各项和特定值差值排序
26、BOM(Browser Object Model)属性对象方法
27、服务端渲染
28、垃圾回收机制
29、eventloop
进程和线程
任务队列
网友评论