题目(JavaScript)
- 1.this指向问题
- 2.声明提前
- 3.局部变量和全局变量
- 4.变量隐式声明
- 5.给基本类型数据添加属性不报错,但访问值时是undefined -- 包装类型
- 6.函数声明优于变量声明
- 7.经典利用闭包问题
- 8.原型对象和继承,及事件处理函数中this的指向
- 9.split
- 10.JS数据类型
- 11.检查浏览器版本有哪些方式
- 12.JS(ES)有哪些内置对象
- 13.JavaScript基本规范
- 14.如何编写高性能JavaScript
- 15.DOM元素e的e.getAttribute(propName) 和 e.propName 有什么联系和区别
- 16.offsetWidth/offsetHeight,clientWidth/clientHeight,scrollWidth/scrollHeight之间的区别
- 17.浏览器的渲染过程,DOM树和渲染树的区别
- 18.重绘和回流(重排)
- 19.如何最小化重排重绘
- 20.script的位置是否会影响首屏显示时间
- 21.对 面向对象 的理解
- 22.介绍JavaScript的原型、原型链,及其特点
- 23.JavaScript如何实现一个类,怎么实例化这个类
- 24.JavaScript如何实现继承
- 25.JavaScript作用域链
- 26.什么是window对象,什么是document对象
- 27.DOM的浏览器兼容性问题/IE的事件处理和W3C的事件处理有哪些区别
- 28.定义一个函数,可以支持所有浏览器中的处理函数的绑定(考虑浏览器的兼容性)
- 29.DOM0, DOM2, DOM3事件处理方式区别
- 30.事件的代理/委托
- 31.如何获得一个DOM元素的绝对位置
- 32.区分客户区坐标,页面坐标,屏幕坐标
- 33.['1', '2', '3'].map(parseInt)答案是多少
- 34.new操作符
1.this指向问题
var length = 10
function fn(){
console.log(this.length)
}
let obj = {
length : 5,
method : function(fn){
fn()
arguments[0]()
}
}
obj.method(fn,1) // 执行结果:10 2
2.声明提前
function fn(a){
console.log(a)
var a = 2
function a(){}
console.log(a)
}
fn(1) // ƒ a(){} 2
3.局部变量和全局变量
var f = true
if(f===true){
var a = 10
}
function fn(){
var b = 20
c = 30
}
fn()
console.log(a) // 10
console.log(c) // 30 未声明则强行创建全局变量b=30
console.log(b) // undefined
4.变量隐式声明
// in:专门判断一个对象中,是否包含指定名称的属性
// 一切全局对象,其实都是window对象的成员
if('a' in window){
var a = 10
}
alert(a) // 10
console.log(a) // 10
console.log(window.a) // 10
console.log(window['a']) // 10
var a1 = 10
window.a2 = 20
window['a3'] = 30
let a4 = 40
delete window.a1
delete window.a2
delete window.a3
delete window.a4
console.log(window)
// a: 10
// a1: 10 用var创建的全局变量无法被delete删除,所以创建对象时建议使用window.变量名;或者使用let
5.给基本类型数据添加属性不报错,但访问值时是undefined – 包装类型
/*
包装类型:
专门封装原始类型,并提供对原始类型执行操作的API
每当对原始类型的值调用方法或访问属性时,都会自动创建包装类型对象
原理:表面上是对原始类型的变量调用方法,实际上是对自动创建的包装类型对象调用方法和访问属性,调用完毕后包装类型对象自动释放
*/
var a = 10
a.pro = 10
/*
typeof a 返回number,则:
new Number(10).pro = 10
随着包装类型对象释放,pro也被释放
*/
console.log(a.pro + a) // NaN
/*
再次访问a.pro,再次new Number(10).pro,返回undefined
undefined + 10 = NaN
*/
var s = 'hello'
s.pro = 'world'
/*
typeof s 返回string,则:
new String('hello').pro = 'world'
随着包装类型对象释放,pro也被释放
*/
console.log(s.pro + s) // undefinedhello,原因同上
/*
再次访问s.pro,再次new String('hello').pro,返回undefined
undefined + 'hello' = undefinedhello
*/
6.函数声明优于变量声明
console.log(typeof fn) // function
function fn(){}
var fn = 10
7.经典利用闭包问题
使用场景:如需给一个函数绑定一个专属变量,需要用到闭包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 实现一段脚本,使得点击对应链接alert出相应的编号 -->
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<script>
var btns = document.getElementsByTagName('button')
var i = 1
for(var btn of btns){
btn.onclick = (function(i){
// 相当于 var i = 全局变量i传入的值1、2、3、4
return function(){ // 利用闭包(i=1)(i=2)(i=3)(i=4)
alert(i)
}
})(i++)
}
</script>
</body>
</html>
或者如下图所示
8.原型对象和继承,及事件处理函数中this的指向
function JSClass(){
this.m_Text = 'division element'
this.m_Element = document.createElement('div')
this.m_Element.innerHTML = this.m_Text
this.m_Element.addEventListener(
'click',this.func//.bind(this)
/*
事件处理函数等回调函数传递时,仅传递.后的函数对象,不会保留.前的对象
因此,当回调函数执行时,this和原理.前的对象无关
而是指向调用该函数的对象,即div.m_Text,也即undefined
若要将this的指向绑定将来new出的新对象,改写为this.func.bind(this)即可,可得division element
*/
)
}
JSClass.prototype.Render = function(){
document.body.appendChild(this.m_Element)
}
JSClass.prototype.func = function(){
alert(this.m_Text)
}
var jc = new JSClass()
jc.Render() // 点击后弹框显示 undefined
jc.func() // 弹框显示 division element
9.split
编写一个函数parseQueryString,把URL参数解析为一个对象
// http://localhost:3000products?key0=0&key1=1&key2=2
// search = location.search
var search = '?key0=0&key1=true&key2=hello'
search = search.slice(1)
var strs = search.split('&')
console.log(strs)
var params = {}
// 遍历每个字符串,按'='切割,第[0]个作属性名,第[1]个作属性值
for(var str of strs){
var arr = str.split('=')
// params[arr[0]] = arr[1]
params[arr[0]] = isNaN(arr[1]) ? arr[1] :Number(arr[1])
}
console.log(params)
/*
params = {
key0:0,
key1:1,
key2:2,
}
*/
10.JS数据类型
- 基本数据类型:undefined、null、boolean、number、string、symbol
- 引用数据类型:object、array、function
11.检查浏览器版本有哪些方式
12.JS(ES)有哪些内置对象
- 数据封装类对象:Object、Array、Boolean、Number、String
- 其他对象:Function、Argument、Math、Date、RgeExp、Error
- ES6新增对象:Symbol、Map、Set、Promise、Proxy、Reflect
13.JavaScript基本规范
- 代码缩进
- 代码段使用{}包裹
- 语句结束使用分号
- 变量和函数在使用前进行声明
- 以大写字母开头命名构造函数,全大写命名常量
- 规范定义JSON对象,补全双引号
14.如何编写高性能JavaScript
- 遵循严格模式:“use strict”
- 将js脚本放在页面底部,加快渲染页面
- 将js脚本成组打包,减少请求
- 使用非阻塞方式下载js脚本
- 尽量使用局部变量来保存全局变量(js代码应该包裹在一个匿名函数自调中)
- 尽量减少使用闭包
- 使用window对象属性方法时,省略window
- 尽量减少对象成员嵌套
- 缓存dom节点的访问(如在jQuery中尽量使用链式操作)
- 避免使用eval()和Function()构造器
- 给setTimeout() 和setInterval() 传递函数而不是字符串作为参数
- 尽量使用直接量创建对象和数组
- 最小化重排重绘,如新增多个平级元素时,应当先加入文档片段中,再将文档片段一次性加入dom树;新增父子元素时, 应先将所有子元素加入父元素,最后一次性加载父元素
15.DOM元素e的e.getAttribute(propName) 和 e.propName 有什么联系和区别
16.offsetWidth/offsetHeight,clientWidth/clientHeight,scrollWidth/scrollHeight之间的区别
- offsetWidth/offsetHeight 返回值包含 content + padding + border,效果与 e.getBoundingClientRect()相同
- clientWidth/clientHeight 返回值只包含 content + padding ,若有滚动条,则不包含滚动条
- scrollWidth/scrollHeight 返回值包含 content + padding + 溢出内容的尺寸
17.浏览器的渲染过程,DOM树和渲染树的区别
浏览器的渲染过程
- 解析HTML构建DOM树,并行请求 css / image / js
- css文件下载完成,开始构建CSSOM(CSS树)
- CSSOM(CSS树)构建结束后,和DOM树一起生成Render Tree(渲染树)
- 布局(layout):计算出每个节点在屏幕中的位置
- 显示(painting):通过显卡把页面画到屏幕上
DOM树和渲染树的区别
- DOM树与HTML标签一一对应,包括head和隐藏元素
- 渲染树不包含head和隐藏元素,大段文本的每一行都是独立节点,每一个节点都有对应的css属性
18.重绘和回流(重排)
- 重绘:当渲染树中的元素外观(如颜色)发生改变,不影响布局时,产生重绘
- 回流:当渲染树中的元素布局(如尺寸、位置、隐藏/显示状态)发生改变时,产生重绘回流
JS获取layout属性值(如:offsetLeft、scrollTop、getComputedStyle等)也会引起回流,因为浏览器需要重新布局来计算出结果
回流必将引起重绘,重绘不一定引起回流
19.如何最小化重排重绘
- 需要对元素进行复杂的操作时,可以先隐藏(display:“none”),操作完成后再显示
- 需要创建多个DOM节点时,使用DocumentFragment创建完后一次性的加入document
- 缓存layout属性值,如:var left = elem.offsetLeft,多次使用left只产生一次回流
- 尽量避免使用table布局(table元素一旦触发回流就会导致table里所有其他的元素回流)
- 避免使用css表达式(expression),因为每次调用都会计算值(包含加载页面)
- 尽量使用css属性简写,如:用border代替border-width, border-style, border-color批量修改元素样式
20.script的位置是否会影响首屏显示时间
21.对 面向对象 的理解
面向对象的三大特点是封装、继承、多态
什么是封装?为什么要封装?如何封装?
什么是继承?为什么要继承?如何继承?
什么是重写?为什么要重写?如何重写?
22.介绍JavaScript的原型、原型链,及其特点
原型:
- JavaScript的所有对象中都包含了 ‘__proto’ 的内部属性,这个属性所对应的就是该对象的原型
- JavaScript的函数对象,除了 ‘__proto’ 之外,还预置了prototype属性
- 当函数对象作为构造函数创建实例时,该prototype属性值将被作为实例对象的原型 ‘__proto’
原型链:
- 当一个对象调用的属性、方法自身不存在时,就会去自己 ‘__proto’ 关联的前辈prototype对象上去找
- 如果没找到,就会去该prototype原型 ‘__proto’ 关联的前辈prototype去找,以此类推,知道找到属性/方法或undefined为止,由此构成了所谓的原型链
- 这种由多级原型对象逐级继承形成的结构,就叫原型链
原型特点: - JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
23.JavaScript如何实现一个类,怎么实例化这个类
1) 构造函数法,用new关键字生成实例对象
优点:e5写法,所有浏览器都支持
缺点:用到了this和prototype,编写复杂,可读性差
2)es6语法糖class,用new关键字生成实例对象
24.JavaScript如何实现继承
25.JavaScript作用域链
- 全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节
- 如果当前作用域没有找到属性或方法,会向上层作用域查找,直至全局函数,这种形式就是作用域链
26.什么是window对象,什么是document对象
- window对象表示当前浏览器的窗口,是JavaScript的顶级对象。我们创建的所有对象、函数、变量都是window对象的成员;window对象的属性和方法全局范围内有效
- document对象是HTML文档的根节点与所有其他节点(元素节点、文本节点、属性节点、注释节点)。document对象使我们可以通过脚本对HTML页面中的所有元素进行访问;document对象是window对象的一部分,可通过window.document属性对其进行访问
27.DOM的浏览器兼容性问题/IE的事件处理和W3C的事件处理有哪些区别
1)事件模型
-
DOM(3个阶段):
① 外向内:捕获
② 目标触发
③ 内向外:冒泡执行
-
IE8(2个阶段):
没有捕获阶段
① 目标触发
② 内向外:冒泡执行
2)事件绑定:
- DOM: elem.addEventListener(‘click’, function(){}, false),第3个参数capture – 是否在捕获阶段就提前触发,默认为false,即都在冒泡阶段后触发
- IE8: elem.attachEvent(‘onclick’, function(){})
3)移除事件:
- DOM:elem.removeEventListener(‘click’, function(){}, false) // 绑定时写了几个参数,移除时也要写几个参数,且参数必须保持一致
- IE8:elem.detachEvent(‘onclick’, function(){}))
4)获得事件对象的方法:
- DOM:elem.addEventListener(‘click’, function(e){}),e指向event
- IE8:不会自动传入事件对象e,事件对象event始终保存在一个全局变量window.event中,elem.attachEvent(‘onclick’, function(){ var e = window.event })
// 兼容所有浏览器的写法
e = e || window.event
5)获得目标元素
- DOM:e.target
- IE8:e.srcElement
// 兼容所有浏览器的写法
var target = e.target || e.srcElement
6)阻止冒泡:
- DOM: e.stopProPagation()
- IE8: e.cancelBubble = true
// 兼容所有浏览器的写法
if(e.cancelBubble === undefined){
// DOM
e.stopPropagation();
}else{
// IE8
e.cancelBubble = true
}
7)阻止默认行为:
- DOM: e.preventDefault()
- IE8: 事件处理函数中 return false
// 兼容所有浏览器的写法
if(typeof e.preventDefault === 'function'){
// DOM
e.preventDefault()
}else{
// IE8
return false
// 或者 e.returnValue = false
}
28.定义一个函数,可以支持所有浏览器中的处理函数的绑定(考虑浏览器的兼容性)
function bindEvent(elem, eventName, handler){
if(typeof elem.attachEvent === 'function'){
// IE8
elem.attachEvent('on'+eventName, function(){
var e = window.event
e.target = e.srcElement
handler.call(this, e)
})
}else{
// DOM
elem.addEventListener(eventName, handler)
}
}
29.DOM0, DOM2, DOM3事件处理方式区别
30.事件的代理/委托
事件委托是指将事件绑定到目标元素的父元素上,利用冒泡机制触发该事件
优点:
- 可以减少事件注册,节省大量内存占用
- 可以将事件应用于动态添加的子元素上
缺点:使用不当会造成事件在不应该触发时触发
31.如何获得一个DOM元素的绝对位置
- elem.offsetLeft 返回元素相对于其定位父级左侧的距离
- elem.offsetTop 返回元素相对于其定位父级顶部的距离
32.区分客户区坐标,页面坐标,屏幕坐标
33.[‘1’, ‘2’, ‘3’].map(parseInt)答案是多少
['1','2','3'].map(parseInt) // [1, NaN, NaN]
['1','2','3'].map(function(elem, index, arr){})
parseInt(e1, e2) // 第一个参数代表要处理的元素,第二个参数代表进制,故:
parseInt('1', 0) // 1, 0为无效值
parseInt('2', '1') // NaN 1进制不可能出现2
parseInt('3', '2') // NaN 2进制不可能出现3