关于前端:2022秋招前端面试题九附答案

6次阅读

共计 13971 个字符,预计需要花费 35 分钟才能阅读完成。

公布订阅模式(事件总线)

形容:实现一个公布订阅模式,领有 on, emit, once, off 办法

class EventEmitter {constructor() {
        // 蕴含所有监听器函数的容器对象
        // 内部结构: {msg1: [listener1, listener2], msg2: [listener3]}
        this.cache = {};}
    // 实现订阅
    on(name, callback) {if(this.cache[name]) {this.cache[name].push(callback);
        }
        else {this.cache[name] = [callback];
        }
    }
    // 删除订阅
    off(name, callback) {if(this.cache[name]) {this.cache[name] = this.cache[name].filter(item => item !== callback);
        }
        if(this.cache[name].length === 0) delete this.cache[name];
    }
    // 只执行一次订阅事件
    once(name, callback) {callback();
        this.off(name, callback);
    }
    // 触发事件
    emit(name, ...data) {if(this.cache[name]) {
            // 创立正本,如果回调函数内持续注册雷同事件,会造成死循环
            let tasks = this.cache[name].slice();
            for(let fn of tasks) {fn(...data);
            }
        }
    }
}
复制代码

原型批改、重写

function Person(name) {this.name = name}
// 批改原型
Person.prototype.getName = function() {}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
// 重写原型
Person.prototype = {getName: function() {}}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // false
复制代码

能够看到批改原型的时候 p 的构造函数不是指向 Person 了,因为间接给 Person 的原型对象间接用对象赋值时,它的构造函数指向的了根构造函数 Object,所以这时候p.constructor === Object,而不是p.constructor === Person。要想成立,就要用 constructor 指回来:

Person.prototype = {getName: function() {}}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // true

复制代码

为什么 0.1 + 0.2 != 0.3,请详述理由

因为 JS 采纳 IEEE 754 双精度版本(64 位),并且只有采纳 IEEE 754 的语言都有该问题。

咱们都晓得计算机示意十进制是采纳二进制示意的,所以 0.1 在二进制示意为

// (0011) 示意循环
0.1 = 2^-4 * 1.10011(0011)
复制代码

那么如何失去这个二进制的呢,咱们能够来演算下

小数算二进制和整数不同。乘法计算时,只计算小数位,整数位用作每一位的二进制,并且失去的第一位为最高位。所以咱们得出 0.1 = 2^-4 * 1.10011(0011),那么 0.2 的演算也根本如上所示,只须要去掉第一步乘法,所以得出 0.2 = 2^-3 * 1.10011(0011)

回来持续说 IEEE 754 双精度。六十四位中符号位占一位,整数位占十一位,其余五十二位都为小数位。因为 0.10.2 都是有限循环的二进制了,所以在小数位开端处须要判断是否进位(就和十进制的四舍五入一样)。

所以 2^-4 * 1.10011...001 进位后就变成了 2^-4 * 1.10011(0011 * 12 次)010。那么把这两个二进制加起来会得出 2^-2 * 1.0011(0011 * 11 次)0100 , 这个值算成十进制就是 0.30000000000000004

上面说一下原生解决办法,如下代码所示

parseFloat((0.1 + 0.2).toFixed(10))
复制代码

事件流

事件流是网页元素接管事件的程序,”DOM2 级事件 ” 规定的事件流包含三个阶段:事件捕捉阶段、处于指标阶段、事件冒泡阶段。
首先产生的事件捕捉,为截获事件提供机会。而后是理论的指标承受事件。最初一个阶段是工夫冒泡阶段,能够在这个阶段对事件做出响应。
尽管捕捉阶段在标准中规定不容许响应事件,然而实际上还是会执行,所以有两次机会获取到指标对象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> 事件冒泡 </title>
</head>
<body>
    <div>
        <p id="parEle"> 我是父元素    <span id="sonEle"> 我是子元素 </span></p>
    </div>
</body>
</html>
<script type="text/javascript">
var sonEle = document.getElementById('sonEle');
var parEle = document.getElementById('parEle');parEle.addEventListener('click', function () {alert('父级 冒泡');}, false);parEle.addEventListener('click', function () {alert('父级 捕捉');}, true);sonEle.addEventListener('click', function () {alert('子级冒泡');}, false);sonEle.addEventListener('click', function () {alert('子级捕捉');}, true);

</script>
复制代码

当容器元素及嵌套元素,即在 捕捉阶段 又在 冒泡阶段 调用事件处理程序时:事件按 DOM 事件流的程序 执行事件处理程序:

  • 父级捕捉
  • 子级捕捉
  • 子级冒泡
  • 父级冒泡

且当事件处于指标阶段时,事件调用程序决定于绑定事件的 书写程序,按下面的例子为,先调用冒泡阶段的事件处理程序,再调用捕捉阶段的事件处理程序。顺次 alert 出“子集冒泡”,“子集捕捉”。

事件是如何实现的?

基于公布订阅模式,就是在浏览器加载的时候会读取事件相干的代码,然而只有理论等到具体的事件触发的时候才会执行。

比方点击按钮,这是个事件(Event),而负责处理事件的代码段通常被称为事件处理程序(Event Handler),也就是「启动对话框的显示」这个动作。

在 Web 端,咱们常见的就是 DOM 事件:

  • DOM0 级事件,间接在 html 元素上绑定 on-event,比方 onclick,勾销的话,dom.onclick = null,同一个事件只能有一个处理程序,前面的会笼罩后面的。
  • DOM2 级事件,通过 addEventListener 注册事件,通过 removeEventListener 来删除事件,一个事件能够有多个事件处理程序,按程序执行,捕捉事件和冒泡事件
  • DOM3 级事件,减少了事件类型,比方 UI 事件,焦点事件,鼠标事件

JS 隐式转换,显示转换

个别非根底类型进行转换时会先调用 valueOf,如果 valueOf 无奈返回根本类型值,就会调用 toString

字符串和数字

  • “+” 操作符,如果有一个为字符串,那么都转化到字符串而后执行字符串拼接
  • “-” 操作符,转换为数字,相减 (-a, a * 1 a/1) 都能进行隐式强制类型转换
[] + {} 和 {} + []
复制代码

布尔值到数字

  • 1 + true = 2
  • 1 + false = 1

转换为布尔值

  • for 中第二个
  • while
  • if
  • 三元表达式
  • ||(逻辑或)&&(逻辑与)右边的操作数

符号

  • 不能被转换为数字
  • 能被转换为布尔值(都是 true)
  • 能够被转换成字符串 “Symbol(cool)”

宽松相等和严格相等

宽松相等容许进行强制类型转换,而严格相等不容许

字符串与数字

转换为数字而后比拟

其余类型与布尔类型

  • 先把布尔类型转换为数字,而后持续进行比拟

对象与非对象

  • 执行对象的 ToPrimitive(对象)而后持续进行比拟

假值列表

  • undefined
  • null
  • false
  • +0, -0, NaN
  • “”

IE 兼容

  • attchEvent(‘on’ + type, handler)
  • detachEvent(‘on’ + type, handler)

代码输入后果

function SuperType(){this.property = true;}

SuperType.prototype.getSuperValue = function(){return this.property;};

function SubType(){this.subproperty = false;}

SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){return this.subproperty;};

var instance = new SubType();
console.log(instance.getSuperValue());
复制代码

输入后果:true

实际上,这段代码就是在实现原型链继承,SubType 继承了 SuperType,实质是重写了 SubType 的原型对象,代之以一个新类型的实例。SubType 的原型被重写了,所以 instance.constructor 指向的是 SuperType。具体如下:

基于 Localstorage 设计一个 1M 的缓存零碎,须要实现缓存淘汰机制

设计思路如下:

  • 存储的每个对象须要增加两个属性:别离是过期工夫和存储工夫。
  • 利用一个属性保留零碎中目前所占空间大小,每次存储都减少该属性。当该属性值大于 1M 时,须要依照工夫排序零碎中的数据,删除一定量的数据保障可能存储下目前须要存储的数据。
  • 每次取数据时,须要判断该缓存数据是否过期,如果过期就删除。

以下是代码实现,实现了思路,然而可能会存在 Bug,然而这种设计题个别是给出设计思路和局部代码,不会须要写出一个无问题的代码

class Store {constructor() {let store = localStorage.getItem('cache')
    if (!store) {
      store = {
        maxSize: 1024 * 1024,
        size: 0
      }
      this.store = store
    } else {this.store = JSON.parse(store)
    }
  }
  set(key, value, expire) {this.store[key] = {date: Date.now(),
      expire,
      value
    }
    let size = this.sizeOf(JSON.stringify(this.store[key]))
    if (this.store.maxSize < size + this.store.size) {console.log('超了 -----------');
      var keys = Object.keys(this.store);
      // 工夫排序
      keys = keys.sort((a, b) => {let item1 = this.store[a], item2 = this.store[b];
        return item2.date - item1.date;
      });
      while (size + this.store.size > this.store.maxSize) {let index = keys[keys.length - 1]
        this.store.size -= this.sizeOf(JSON.stringify(this.store[index]))
        delete this.store[index]
      }
    }
    this.store.size += size

    localStorage.setItem('cache', JSON.stringify(this.store))
  }
  get(key) {let d = this.store[key]
    if (!d) {console.log('找不到该属性');
      return
    }
    if (d.expire > Date.now) {console.log('过期删除');
      delete this.store[key]
      localStorage.setItem('cache', JSON.stringify(this.store))
    } else {return d.value}
  }
  sizeOf(str, charset) {
    var total = 0,
      charCode,
      i,
      len;
    charset = charset ? charset.toLowerCase() : '';
    if (charset === 'utf-16' || charset === 'utf16') {for (i = 0, len = str.length; i < len; i++) {charCode = str.charCodeAt(i);
        if (charCode <= 0xffff) {total += 2;} else {total += 4;}
      }
    } else {for (i = 0, len = str.length; i < len; i++) {charCode = str.charCodeAt(i);
        if (charCode <= 0x007f) {total += 1;} else if (charCode <= 0x07ff) {total += 2;} else if (charCode <= 0xffff) {total += 3;} else {total += 4;}
      }
    }
    return total;
  }
}
复制代码

10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路

这个问题置信很多人会第一工夫想到 Promise.all,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路

// 以下是不残缺代码,着重于思路 非 Promise 写法
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {if (success) {
         success++
         if (success + errorCount === 10) {console.log(datas)
         } else {datas.push(res.data)
         }
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
             throw Error('失败三次')
         }
     }
})
// Promise 写法
let errorCount = 0
let p = new Promise((resolve, reject) => {if (success) {resolve(res.data)
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
            reject(error)
         } else {resolve(error)
         }
     }
})
Promise.all([p]).then(v => {console.log(v);
});
复制代码

说一下原型链和原型链的继承吧

  • 所有一般的 [[Prototype]] 链最终都会指向内置的 Object.prototype,其蕴含了 JavaScript 中许多通用的性能
  • 为什么能创立“类”,借助一种非凡的属性:所有的函数默认都会领有一个名为 prototype 的共有且不可枚举的属性,它会指向另外一个对象,这个对象通常被称为函数的原型
function Person(name) {this.name = name;}

Person.prototype.constructor = Person
复制代码
  • 在产生 new 结构函数调用时,会将创立的新对象的 [[Prototype]] 链接到 Person.prototype 指向的对象,这个机制就被称为原型链继承
  • 办法定义在原型上,属性定义在构造函数上
  • 首先要说一下 JS 原型和实例的关系:每个构造函数(constructor)都有一个原型对象(prototype),这个原型对象蕴含一个指向此构造函数的指针属性,通过 new 进行结构函数调用生成的实例,此实例蕴含一个指向原型对象的指针,也就是通过 [[Prototype]] 链接到了这个原型对象
  • 而后说一下 JS 中属性的查找:当咱们试图援用实例对象的某个属性时,是依照这样的形式去查找的,首先查找实例对象上是否有这个属性,如果没有找到,就去结构这个实例对象的构造函数的 prototype 所指向的对象下来查找,如果还找不到,就从这个 prototype 对象所指向的构造函数的 prototype 原型对象下来查找
  • 什么是原型链:这样逐级查找形似一个链条,且通过  [[Prototype]] 属性链接,所以被称为原型链
  • 什么是原型链继承,类比类的继承:当有两个构造函数 A 和 B,将一个构造函数 A 的原型对象的,通过其 [[Prototype]] 属性链接到另外一个 B 构造函数的原型对象时,这个过程被称之为原型继承。

标准答案更正确的解释

什么是原型链?

当对象查找一个属性的时候,如果没有在本身找到,那么就会查找本身的原型,如果原型还没有找到,那么会持续查找原型的原型,直到找到 Object.prototype 的原型时,此时原型为 null,查找进行。
这种通过 通过原型链接的逐级向上的查找链被称为原型链

什么是原型继承?

一个对象能够应用另外一个对象的属性或者办法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样依据原型链的规定,如果查找一个对象属性且在本身不存在时,就会查找另外一个对象,相当于一个对象能够应用另外一个对象的属性和办法了。

如果 new 一个箭头函数的会怎么样

箭头函数是 ES6 中的提出来的,它没有 prototype,也没有本人的 this 指向,更不能够应用 arguments 参数,所以不能 New 一个箭头函数。

new 操作符的实现步骤如下:

  1. 创立一个对象
  2. 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的 prototype 属性)
  3. 指向构造函数中的代码,构造函数中的 this 指向该对象(也就是为这个对象增加属性和办法)
  4. 返回新的对象

所以,下面的第二、三步,箭头函数都是没有方法执行的。

实现数组原型办法

forEach

语法:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

参数:

callback:为数组中每个元素执行的函数,该函数承受 1 - 3 个参数currentValue: 数组中正在解决的以后元素index(可选): 数组中正在解决的以后元素的索引array(可选): forEach() 办法正在操作的数组 thisArg(可选): 当执行回调函数 callback 时,用作 this 的值。

返回值:undefined

Array.prototype.forEach1 = function(callback, thisArg) {if(this == null) {throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
    }
    // 创立一个新的 Object 对象。该对象将会包裹 (wrapper) 传入的参数 this(以后数组)。const O = Object(this);
    // O.length >>> 0 无符号右移 0 位
    // 意义:为了保障转换后的值为正整数。// 其实底层做了 2 层转换,第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型
    const len = O.length >>> 0;
    let k = 0;
    while(k < len) {if(k in O) {callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
}
复制代码

map

语法:arr.map(callback(currentValue [, index [, array]])[, thisArg])

参数:与 forEach() 办法一样

返回值:一个由原数组每个元素执行回调函数的后果组成的新数组。

Array.prototype.map1 = function(callback, thisArg) {if(this == null) {throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let newArr = [];  // 返回的新数组
    let k = 0;
    while(k < len) {if(k in O) {newArr[k] = callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
    return newArr;
}
复制代码

filter

语法:arr.filter(callback(element [, index [, array]])[, thisArg])

参数:

callback: 用来测试数组的每个元素的函数。返回 true 示意该元素通过测试,保留该元素,false 则不保留。它承受以下三个参数:element、index、array,参数的意义与 forEach 一样。

thisArg(可选): 执行 callback 时,用于 this 的值。

返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

Array.prototype.filter1 = function(callback, thisArg) {if(this == null) {throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let newArr = [];  // 返回的新数组
    let k = 0;
    while(k < len) {if(k in O) {if(callback.call(thisArg, O[k], k, O)) {newArr.push(O[k]);
            }
        }
        k++;
    }
    return newArr;
}
复制代码

some

语法:arr.some(callback(element [, index [, array]])[, thisArg])

参数:

callback: 用来测试数组的每个元素的函数。承受以下三个参数:element、index、array,参数的意义与 forEach 一样。

thisArg(可选): 执行 callback 时,用于 this 的值。
返回值:数组中有至多一个元素通过回调函数的测试就会返回 true;所有元素都没有通过回调函数的测试返回值才会为 false

Array.prototype.some1 = function(callback, thisArg) {if(this == null) {throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let k = 0;
    while(k < len) {if(k in O) {if(callback.call(thisArg, O[k], k, O)) {return true}
        }
        k++;
    }
    return false;
}
复制代码

reduce

语法:arr.reduce(callback(preVal, curVal[, curIndex [, array]])[, initialValue])

参数:

callback: 一个“reducer”函数,蕴含四个参数:

preVal:上一次调用 callback 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]

curVal:数组中正在解决的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]

curIndex(可选):数组中正在解决的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。

array(可选):用于遍历的数组。
initialValue(可选): 作为第一次调用 callback 函数时参数 preVal 的值。若指定了初始值 initialValue,则 curVal 则将应用数组第一个元素;否则 preVal 将应用数组第一个元素,而 curVal 将应用数组第二个元素。
返回值:应用“reducer”回调函数遍历整个数组后的后果。

Array.prototype.reduce1 = function(callback, initialValue) {if(this == null) {throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    let accumulator = initialValue;
    // 如果第二个参数为 undefined 的状况下,则数组的第一个有效值(非 empty)作为累加器的初始值
    if(accumulator === undefined) {while(k < len && !(k in O)) {k++;}
        // 如果超出数组界线还没有找到累加器的初始值,则 TypeError
        if(k >= len) {throw new TypeError('Reduce of empty array with no initial value');
        }
        accumulator = O[k++];
    }
    while(k < len) {if(k in O) {accumulator = callback(accumulator, O[k], k, O);
        }
        k++;
    }
    return accumulator;
}
复制代码

类数组转化为数组的办法

题目形容: 类数组领有 length 属性 能够应用下标来拜访元素 然而不能应用数组的办法 如何把类数组转化为数组?

实现代码如下:

const arrayLike=document.querySelectorAll('div')

// 1. 扩大运算符
[...arrayLike]
// 2.Array.from
Array.from(arrayLike)
// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)
// 4.Array.apply
Array.apply(null, arrayLike)
// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)

复制代码

new 操作符

题目形容: 手写 new 操作符实现

实现代码如下:

function myNew(fn, ...args) {let obj = Object.create(fn.prototype);
  let res = fn.call(obj, ...args);
  if (res && (typeof res === "object" || typeof res === "function")) {return res;}
  return obj;
}
用法如下:// // function Person(name, age) {
// //   this.name = name;
// //   this.age = age;
// // }
// // Person.prototype.say = function() {// //   console.log(this.age);
// // };
// // let p1 = myNew(Person, "lihua", 18);
// // console.log(p1.name);
// // console.log(p1);
// // p1.say();
复制代码

写代码:实现函数可能深度克隆根本类型

浅克隆:

function shallowClone(obj) {let cloneObj = {};

  for (let i in obj) {cloneObj[i] = obj[i];
  }

  return cloneObj;
}
复制代码

深克隆:

  • 思考根底类型
  • 援用类型

    • RegExp、Date、函数 不是 JSON 平安的
    • 会失落 constructor,所有的构造函数都指向 Object
    • 破解循环援用
function deepCopy(obj) {if (typeof obj === 'object') {var result = obj.constructor === Array ? [] : {};

    for (var i in obj) {result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  } else {var result = obj;}

  return result;
}
复制代码

ES6 中模板语法与字符串解决

ES6 提出了“模板语法”的概念。在 ES6 以前,拼接字符串是很麻烦的事件:

var name = 'css'   
var career = 'coder' 
var hobby = ['coding', 'writing']
var finalString = 'my name is' + name + ', I work as a' + career + ', I love' + hobby[0] + 'and' + hobby[1]
复制代码

仅仅几个变量,写了这么多加号,还要时刻小心外面的空格和标点符号有没有跟错中央。然而有了模板字符串,拼接难度直线降落:

var name = 'css'   
var career = 'coder' 
var hobby = ['coding', 'writing']
var finalString = `my name is ${name}, I work as a ${career} I love ${hobby[0]} and ${hobby[1]}`
复制代码

字符串不仅更容易拼了,也更易读了,代码整体的品质都变高了。这就是模板字符串的第一个劣势——容许用 ${}的形式嵌入变量。但这还不是问题的要害,模板字符串的要害劣势有两个:

  • 在模板字符串中,空格、缩进、换行都会被保留
  • 模板字符串齐全反对“运算”式的表达式,能够在 ${}里实现一些计算

基于第一点,能够在模板字符串里无障碍地间接写 html 代码:

let list = `    <ul>        <li> 列表项 1 </li>        <li> 列表项 2 </li>    </ul>`;
console.log(message); // 正确输入,不存在报错
复制代码

基于第二点,能够把一些简略的计算和调用丢进 ${} 来做:

function add(a, b) {const finalString = `${a} + ${b} = ${a+b}`
  console.log(finalString)
}
add(1, 2) // 输入 '1 + 2 = 3'
复制代码

除了模板语法外,ES6 中还新增了一系列的字符串办法用于晋升开发效率:

(1)存在性断定:在过来,当判断一个字符 / 字符串是否在某字符串中时,只能用 indexOf > -1 来做。当初 ES6 提供了三个办法:includes、startsWith、endsWith,它们都会返回一个布尔值来通知你是否存在。

  • includes:判断字符串与子串的蕴含关系:
const son = 'haha' 
const father = 'xixi haha hehe'
father.includes(son) // true
复制代码
  • startsWith:判断字符串是否以某个 / 某串字符结尾:
const father = 'xixi haha hehe'
father.startsWith('haha') // false
father.startsWith('xixi') // true
复制代码
  • endsWith:判断字符串是否以某个 / 某串字符结尾:
const father = 'xixi haha hehe'
  father.endsWith('hehe') // true
复制代码

(2)主动反复:能够应用 repeat 办法来使同一个字符串输入屡次(被间断复制屡次):

const sourceCode = 'repeat for 3 times;'
const repeated = sourceCode.repeat(3) 
console.log(repeated) // repeat for 3 times;repeat for 3 times;repeat for 3 times;
复制代码

iframe 有那些长处和毛病?

iframe 元素会创立蕴含另外一个文档的内联框架(即行内框架)。

长处:

  • 用来加载速度较慢的内容(如广告)
  • 能够使脚本能够并行下载
  • 能够实现跨子域通信

毛病:

  • iframe 会阻塞主页面的 onload 事件
  • 无奈被一些搜索引擎索辨认
  • 会产生很多页面,不容易治理

const 对象的属性能够批改吗

const 保障的并不是变量的值不能改变,而是变量指向的那个内存地址不能改变。对于根本类型的数据(数值、字符串、布尔值),其值就保留在变量指向的那个内存地址,因而等同于常量。

但对于援用类型的数据(次要是对象和数组)来说,变量指向数据的内存地址,保留的只是一个指针,const 只能保障这个指针是固定不变的,至于它指向的数据结构是不是可变的,就齐全不能管制了。

正文完
 0