【面试向】极简前端知识点(不断更新)
2018-05-16 本文已影响101人
_月光临海
强烈推荐:https://github.com/mqyqingfeng/Blog
别人总结的前端面试题:https://github.com/BearD01001/front-end-QA-to-interview
CS / BS 架构
- CS 客户端 / 服务器架构
- BS 浏览器 / 服务器架构
RESTful
将URL看作资源,URL 中用 名词 表示。各种操作通过不同的请求方式来识别。目的是 只看接口和请求方式就能知道这是干嘛的,比如
顺便知道一下,除了 GET,POST 外,还有 HEAD, PUT,PATCH,DELETE 等请求方式
GET /api/libraries/123/books?keyword=game&sort=price&limit=10&offset=0
含义就是:从一堆图书馆中,找到第 123 个图书馆的书,在书里查询所有关键词为 game ,以价格排序,从 0 开始的前 10 条数据
幂等 / 非幂等
- 幂等:多次请求结果一样,比如一条更新的请求,将数值更新为 5,执行几次都是更新为 5;
- 非幂等:添加。原来有三条数据,随着添加操作,数据数也随之增加
请求
-
项目中的一个查询请求(这里从 RESTful 的角度讲,不应该用 POST)
一次请求
关于 meta 标签的 content="width=device-width"
目前总结的理解方式为:让你当前正在写的 html 的宽度等于设备宽度
Object.freeze() 冻结
- 无法添加属性,但对于非基本类型(Array,Object)的属性依旧可以添加。
- 彻底冻结一个对象(一冻到底)
var obj = {
arr: [1, 2]
}
function freezen(obj) {
Object.freeze(obj);
Object.keys(obj).forEach(function(it, i) {
if(typeof obj[it] === 'object') {
freezen(obj[it])
}
})
}
freezen(obj)
类数组转数组
- Array.from(likeArr)
- [...likeArr]
- Array.prototype.slice.call()
关于 var let 和 for 循环
- for 循环条件中 var 声明的变量等同于全局变量
- for 循环条件中 let 声明的变量不是全局变量(全局中取不到),算作是 for 循环 {} 的 父级变量,不会影响 {} 中 let 声明同样的变量;这一点跟函数不一样,参数声明了 x ,函数体中需要注意 暂时性死区
- var 声明的全局变量是 window 对象的一个属性,但 let 声明的对象不是
map,foreach,$.each,each
# map
数组.map(function(值,下标,数组) {
console.log(arguments)
})
# foreach
数组.foreach(function(值,下标,数组) {
console.log(arguments)
})
# $.each(jQuery)
$.each(任何可遍历的对象, function(key, value) {
console.log(arguments)
})
# each(jQuery)
DOM对象.each(function(key, value) {
console.log(arguments)
})
箭头函数两句话攻略
- 箭头函数没有自己的 this,arguments,super,由于没有自己的 this,也就不能使用 bind,call,apply 更改 this 的指向
- 箭头函数体只有一条语句时自带 return,且 return 可省略
事件委托
- 提高性能,有牌面
祖先.addEventListener('click', function(e){
var target = e.target || e.srcElement;
if (!!target && target.nodeName.toLowerCase()==='目标元素') {
console.log(target.innerHTML)
}
})
关于获取属性的 []
和 .
的区别
-
[]
可以用变量,数字和保留字获取属性,ES6 还允许表达式
Object.assign()
Object.assign(目标对象,源...)
- 合并 可枚举 的 自身 属性
- 浅拷贝,即引用时源改变了则目标也改变
Object.defineProperty() / Object.defineProperties() 添加属性描述
Object.defineProperty(目标对象,属性名,{属性描述})
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});
Object.defineProperties(目标对象,属性组及属性描述)
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
目标对象.hasOwnProperty()
-
目标对象.hasOwnProperty(目标属性)
obj 自身 是否含有目标属性,不是原型上,返回布尔值
Object.getPrototypeOf() / Object.setPrototypeOf()
-
Object.getPrototypeOf(目标对象)
获取目标对象的原型 -
Object.setPrototypeOf(目标对象,对象原型)
设置目标对象的原型
Object.create()
-
Object.create(新对象的原型,{新对象的属性:{属性描述}})
(后者可选)
# 创建新对象 clone,其原型为 obj 的原型,添加 newProp 属性
const obj = {
'a': 'aa',
222: 3123,
'unenumerable': '不可枚举属性'
}
const clone = Object.create(Object.getPrototypeOf(obj), {
'newProp': {
value: '新值',
enumerable: true
}
});
Object.getOwnPropertyDescriptors(clone)
// newProp:{value: undefined, writable: false, enumerable: true, configurable: false}
// 未指定的属性均为 false
也可以:
const obj = {
'a': 'aa',
222: 3123,
'unenumerable': '不可枚举属性'
}
Object.defineProperty(obj, 'unenumerable', {
enumerable: false
})
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
Object.getOwnPropertyDescriptors(clone)
结果:

这样就等同于 浅克隆 了一个 obj 对象 clone
Object.getOwnPropertyDescriptor()
-
Object.getOwnPropertyDescriptor(目标对象,目标对象属性)
获取目标对象自身的属性描述
const obj = {
'a': 'aa',
222: 3123
}
Object.getOwnPropertyDescriptor(obj, 'a')
// {value: "aa", writable: true, enumerable: true, configurable: true}
-
Object.getOwnPropertyDescriptors(目标对象)
获取目标对象自身的全部属性描述
const obj = {
'a': 'aa',
222: 3123,
'unenumerable':'不可枚举'
}
Object.defineProperty(obj,'unenumerable',{enumerable:false}) // 设置为不可枚举
Object.getOwnPropertyDescriptors(obj)
/**
* 222: {value: 3123, writable: true, enumerable: true, configurable: true}
* a: {value: "aa", writable: true, enumerable: true, configurable: true}
* unenumerable: {value: "不可枚举", writable: true, enumerable: false, configurable: true}
**/
for ... in / for ... of / Object.keys / Object.values / Object.entries / Object.getOwnPropertyNames(obj)
- for ... in 自身及继承 的所有 可枚举 属性,不含 Symbol
- Object.keys 返回数组,自身 所有 可枚举 属性(不含 Symbol 属性)的键名
- Object.getOwnPropertyNames(obj) 自身,可枚举 及 不可枚举 的属性名(不含 Symbol)
名称 | 目标 | 类别 |
---|---|---|
for...in... | 自身及继承的属性名或索引 | 可枚举 |
Object.keys | 自身 | 可枚举 |
Object.values | 自身 | 可枚举 |
Object.entries | 自身 | 可枚举 |
Object.getOwnPropertyNames(obj) | 自身 | 可枚举 / 不可枚举 |
- for ... of ...:遍历具有 Symbol.iterator 接口对象自身具有数字索引的值
- for ... in ...:遍历对象自身及原型上可枚举的属性名或索引
let arr = [3, 5, 7];
arr.foo = 'hello';
Array.prototype.baz='world';
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo", "baz"
}
for (let i of arr) {
console.log(i); // "3", "5", "7"
}
构造函数原型对象上的方法和属性是给实例调用的,构造函数本身无法调用
Set / Map 数据结构
- Set:没有重复值的 Array
- Map:常规的 Object 是key:value形式,Map 支持 Object 等作为 key
外部脚本 <script> 标签及 defer,async
- 渲染引擎遇到 <script> 标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
// module_test.js
let delay = +new Date();
console.log('外部脚本');
while(+new Date() - delay < 3000);
// index.html
<script src="js/module_test.js" type="text/javascript"></script>
<script type="text/javascript">
console.log('本地脚本')
</script>
// 结果
外部脚本
---- 3s later ----
本地脚本
- defer 和 async :<script> 标签打开 defer 或 async 属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
- 区别:defer 要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async 一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer 是“渲染完再执行”,async 是“下载完就执行”。另外,如果有多个 defer 脚本,会按照它们在页面出现的顺序加载,而多个 async脚本是不能保证加载顺序的。
解决 jQuery 中 $ 冲突
- jQuery.noConflict()
var j = jQuery.noConflict();
// 基于 jQuery 的代码
j("div p").hide();
// 基于其他库的 $() 代码
$("content").style.display = 'none';
解决 jQuery 中 $ 原理
- 方法加在 jQuery 上,然后通过
window.jQuery = window.$ = jQuery
数组操作 / 字符串操作
- 数组操作
名称 | 用法 | 返回 |
---|---|---|
concat() | arrayObject.concat(arrayX,arrayX,......,arrayX) | 新数组 |
join() | arrayObject.join(分隔符) | 字符串 |
pop() | arrayObject.pop() | 最后一个元素 |
push() | arrayObject.push(newelement1,newelement2,....,newelementX) | 添加后的长度 |
reverse() | arrayObject.reverse() | 改变原数组 |
shift() | arrayObject.shift() | 第一个元素 |
slice() | arrayObject.slice(start,end) | 不改变原数组,返回新数组,支持负数 |
sort() | arrayObject.sort(function) | 改变原数组 |
splice() | arrayObject.splice(index,howmany,item1,.....,itemX) | 改变原数组 |
- 字符串操作
名称 | 描述 | 返回 |
---|---|---|
charAt() | 返回在指定位置的字符 | |
concat() | 连接字符串 | |
indexOf() | 检索字符串 | 布尔值 |
match() | 找到一个或多个正则表达式的匹配 | 布尔值 |
replace() | 替换与正则表达式匹配的子串 | 新字符串 |
search() | 检索与正则表达式相匹配的值 | 第一个匹配子串的起始位置 |
slice() | 提取字符串的片断,并在新的字符串中返回被提取的部分 | |
split() | 把字符串分割为字符串数组 | |
substr() | 从起始索引号提取字符串中 指定数目 的字符 | |
substring() | 提取字符串中两个 指定的索引号 之间的字符 |
回流(reflow)和重绘(repaint)
- 回流:更改了 DOM 结构(display:none),回流也会触发重绘
- 重绘:变个颜色,加个边框之类的
表格的 cellpadding 和 cellspacing

确切的判断一个对象 / 数组 / null 等的类型
- Array.isArray()
或Object.prototype.toString.call()==='[object Object]' Object.prototype.toString.call()==='[object Array]' Object.prototype.toString.call()==='[object Set]' Object.prototype.toString.call()==='[object Map]' Object.prototype.toString.call()==='[object Symbol]' Object.prototype.toString.call()==='[object String]' Object.prototype.toString.call()==='[object Null]' Object.prototype.toString.call()==='[object Undefined]' Object.prototype.toString.call()==='[object Boolean]' Object.prototype.toString.call()==='[object Number]'
- 判断
NaN
用isNaN()
,或者Object.is()
=== 和 Object.is
区别 | === | Object |
---|---|---|
+0,-0 | true | false |
NaN,NaN | false | true |
原生 ajax
- get
var xhr = new XMLHttpRequest();
xhr.open('get','getStar.php?starName='+name,true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState==4 && xhr.status==200) {
console.log(xhr.responseText); //输入相应的内容
}
}
- post
var xhr = new XMLHttpRequest();
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.open('post', '02.post.php' ,true);
xhr.send('name=fox&age=18');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
创建对象的几种模式
- 工厂模式:无法判断对象类型(对比构造函数模式)
function createPerson(){
var Child = new Object();
Child.name = "欲泪成雪";
Child.age = "20";
return Child;
};
var x = createPerson();
- 构造函数模式:可以通过
x instanceof Person
得知x
是Person
,而工厂模式则无法知道x
是Person
;
function Person(){
this.name = "欲泪成雪";
this.age = "20";
this.say = function(){
alert("Hello!")
}
};
function Fruit(){
this.name = "水果";
this.price = "13";
};
var x = new Person();
var y = new Fruit();
但这种模式每次实例化都会重新初始化属性 arr
和方法 say
,并且不同实例的 arr
和 say
并不是一个玩意
function Person() {
this.name = "欲泪成雪";
this.age = "20";
this.arr = [0,1];
this.say = function(){
alert("Hello")
}
};
var Tom = new Person();
var Jerry = new Person();
console.log(Tom.name === Jerry.name) // true
console.log(Tom.arr === Jerry.arr) // false
console.log(Tom.say === Jerry.say) // false
- 原型模式:如下这种形式,每个实例的属性和方法都相同
function Person(){};
Parent.prototype.name = "欲泪成雪";
Parent.prototype.age = "20";
var x = new Person();
- 混合模式(构造 + 原型)
function Person(){
this.name = "欲泪成雪";
this.age = 22;
};
Person.prototype.lev = function(){
return this.name;
};
var x = new Person();
get / post
get | post |
---|---|
URL中发送 | 消息主体 |
可缓存 | 不可缓存 |
可历史记录 | 不可保留为历史记录 |
可书签 | 不可书签 |
有长度限制 | 没有长度限制 |
关于 getElementsByTagName 和 querySelectorAll
- querySelectorAll 可直接放选择器 '.abc'
- querySelectorAll 性能不如前者
- querySelectorAll 静态查找
<ul>
<li>aaa</li>
<li>ddd</li>
<li>ccc</li>
</ul>
<script type="text/javascript">
var ul = document.getElementsByTagName('ul')[0];
var get_num = ul.getElementsByTagName("li");
var query_num = ul.querySelectorAll('li');
console.log(get_num.length, query_num.length) // 3,3
ul.appendChild(document.createElement("li")); // 动态添加 li
console.log(get_num.length, query_num.length) // 4,3
</script>
HTML5 新特性
- 新增选择器 document.querySelector、document.querySelectorAll
- 拖拽释放(Drag and drop) API
- 媒体播放的 video 和 audio
- 本地存储 localStorage 和 sessionStorage
- 离线应用 manifest
- 桌面通知 Notifications
- 语意化标签 article、footer、header、nav、section
- 增强表单控件 calendar、date、time、email、url、search
- 地理位置 Geolocation
- 多任务 webworker
- 全双工通信协议 websocket
- 历史管理 history
- 跨域资源共享(CORS) Access-Control-Allow-Origin
- 页面可见性改变事件 visibilitychange
- 跨窗口通信 PostMessage
- Form Data 对象
- 绘画 canvas
响应式布局
- CSS3
@media screen and (min-width:960px) and (max-width:1200px){
body{background:yellow;}
}
- 也可以这样
<link rel="stylesheet" href="styleA.css" media="screen">
<link rel="stylesheet" href="styleB.css" media="screen and (max-width: 800px)">
<link rel="stylesheet" href="styleC.css" media="screen and (max-width: 600px)">
链式调用原理
function Foo(){}
Foo.prototype = {
css:function(){
console.log("设置css样式");
return this;
},
show:function(){
console.log("将元素显示");
return this;
},
hide:function(){
console.log("将元素隐藏");
}
};
var foo = new Foo();
foo.css().css().show().hide();
flex
- 关键词:容器,项目,主轴,交叉轴
- 容器和项目是父子集
- 容器
display:flex
后,float
,clear
,vertical-align
失效 - 容器
属性名 | 描述 |
---|---|
flex-direction | 主轴的方向 |
flex-wrap | 如何换行 |
flex-flow | 上两个的缩写 |
justify-content | 项目在主轴的对齐方式 |
align-items | 项目在交叉轴的对齐方式 |
- 项目
属性名 | 描述 |
---|---|
order | 项目排列顺序,越小越靠前 |
flex-grow | 项目放大比例 |
flex-shrink | 项目缩小比例 |
flex-basis | 属性定义了在分配多余空间之前,项目占据的主轴空间 |
flex | 上三个的缩写 |
align-self | 项目在交叉轴上的对齐方式 |
vue 虚拟 DOM
- 直接操作 DOM 成本高,通过 JavaScript 创建一个记录 DOM 的对象,之后一次渲染这个对象
- 先定义一个构造函数
//虚拟dom,参数分别为标签名、属性对象、子DOM列表
var VElement = function(tagName, props, children) {
//保证只能通过如下方式调用:new VElement
if (!(this instanceof VElement)) {
return new VElement(tagName, props, children);
}
//可以通过只传递tagName和children参数
if (util.isArray(props)) {
children = props;
props = {};
}
//设置虚拟dom的相关属性
this.tagName = tagName;
this.props = props || {};
this.children = children || [];
this.key = props ? props.key : void 666;
var count = 0;
util.each(this.children, function(child, i) {
if (child instanceof VElement) {
count += child.count;
} else {
children[i] = '' + child;
}
count++;
});
this.count = count;
}
- 生成实例,用 JavaScript 表示 DOM 结构
var vdom = velement('div', { 'id': 'container' }, [
velement('h1', { style: 'color:red' }, ['simple virtual dom']),
velement('p', ['hello world']),
velement('ul', [velement('li', ['item #1']), velement('li', ['item #2'])]),
]);
- 上面的 JavaScript 代码就代表了如下的 DOM 结构
<div id="container">
<h1 style="color:red">simple virtual dom</h1>
<p>hello world</p>
<ul>
<li>item #1</li>
<li>item #2</li>
</ul>
</div>
Vue 的计算属性 computed 和监听属性 watch
- computed 可以新建一个属性
- watch 只能用 data 中已有的属性
<div id="demo">{{ fullName }}</div>
// watch
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) { // firstName 和 lastName 都是 data 中已有的属性
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
// computed
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () { // fullName 在 data 中并没有
return this.firstName + ' ' + this.lastName
}
}
})
margin 塌陷
- 父子集关系:
1.父级overflow:hidden
2.父级添加border
3.父级添加padding
- 兄弟集关系:
1.任意兄弟添加display:inline-block
2.下面的添加float:left
图片下的小空白

- 红框为父级
<div>
,而 图片和文字 都是以父级的基线baseline
对齐,基线就是xxx
的下边界,因此修改图片的对齐方式vertical-align
即可 - 图片设置:
1.display:block
2.vertical-align:top - 父级设置:
1.font-size:0 -
父子同时设置:
1.float:left
修改 span 的 vertical-align
BFC 是什么?
- 可以理解为定义一个环境,在这个环境内的元素不论怎么调整都不会影响环境外部的样式
- 形成条件
1.浮动元素,float 除 none 以外的值;
2.绝对定位元素,position(absolute,fixed);
3.display 为以下其中之一的值 inline-blocks,table-cells,table-captions;
4.overflow 除了 visible 以外的值(hidden,auto,scroll)
公有方法,私有方法,静态属性
- 公有方法:实例可以调用
- 私有方法:只能在构造函数内调用,一般用于对构造函数内的属性或方法进行修改
function Foo(){
// 公有方法
this.name = function(){
alert("Hello")
}
// 私有方法
function age(){
alert("World")
}
}
- 静态属性:只能由构造函数自己调用,而不能被实例调用的属性
# ES5
function Foo(){}
Foo.age = 12; // 静态属性
Foo.getAge = function(){ // 静态方法
console.log(this.age)
}
# ES6
class Foo{
static age(){ // 静态方法
console.log(12)
}
}
Foo.sex = 'boy' // 静态属性(ES6 类内没有静态属性)
ES6 继承
class Son extends Dad {}
数据属性 / 访问器属性
- 数据属性:
1.configurable
2.enumerable
3.writable
4.value - 访问器属性:
1.configurable
2.enumerable
3.get
4.set
实现一个深拷贝
function deepClone(obj) {
var newObj = obj instanceof Array ? [] : {};
//obj属于基本数据类型,直接返回obj
if(typeof obj !== 'object') {
return obj;
} else {
//obj属于数组或对象,遍历它们
for(var i in obj) {
newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]):obj[i];
}
}
return newObj;
}
取出嵌套数组的全部数值
- 这个思路跟上一个很像,所以放在一起
// 常规版
let newArr = [];
function getArr(arr) {
for(let i = 0; i < arr.length; i++) {
if(arr[i] instanceof Array) {
getArr(arr[i])
} else {
newArr.push(arr[i])
}
}
}
const arr = ["a", "b", "c", "d", ["aa", "bb"]];
getArr(arr);
console.log(newArr)
- generator 版
const arr = ["a", "b", "c", "d", ["aa", "bb"]];
function* tree(item) {
if(Array.isArray(item)) { // 如果是数组,就依次取出,再调用自己
for(var i = 0; i < item.length; i++) {
yield* tree(item[i])
}
} else { // 如果不是数组,直接输出
yield item
}
}
const it = tree(arr);
// 可以
for(var k of it) {
console.log(k)
}
// 也可以
console.log([...it]) // ["a", "b", "c", "d", "aa", "bb"]
克隆对象及原型链
// 写法一
const clone1 = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
);
// 写法二
const clone2 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
几道面试题
- 一直以来我都认为这种题意义不大,然而每次遇到这种题时却发现答案总是摇摆不定,归根结底还是基础不够扎实,因此只好强忍着恶心再来一点点分析
function create(obj){
obj.name = '粉粉';
obj = new Object();
obj.name = '娇娇';
}
var person = new Object();
create(person);
console.log(person.name); // 粉粉
- 这个也得改一下,先是把函数改成这样:
function create() {
var obj;
obj.name = '粉粉';
obj = new Object();
obj.name = '娇娇';
}
- 而调用
create(person)
变成这样:
function create() {
var obj = person; // 此时 obj 和 person 指向一块内存
obj.name = '粉粉'; // 为同一块内存添加属性,这是 obj 和 person 共有的属性
obj = new Object(); // 然后让 obj 指向另一块内存
obj.name = '娇娇'; // obj 已经指向另一块内存,再为 obj 添加属性当然跟 person 无关了
}
var a = '小旭';
(function(){
console.log(typeof a) // undefined
console.log(typeof fun1) // function
console.log(typeof fun2) // undefined
var a = '旭旭';
function fun1(){};
var fun2 = function(){}
console.log(typeof a); // string
console.log(typeof fun1); // function
console.log(typeof fun2); // function
})(window)
- 这题主要是变量提升,与下面这种写法是等价的,再看不懂就去面壁
var a = '小旭';
(function(){
var a;
function fun1(){};
var fun2;
console.log(typeof a) // undefined
console.log(typeof fun1) // function
console.log(typeof fun2) // undefined
a = '旭旭';
fun2 = function(){}
console.log(typeof a); // string
console.log(typeof fun1); // function
console.log(typeof fun2); // function
})(window)
var foo = {
bar:function(){
return this.baz;
},
baz:1
};
(function(){
console.log(arguments[0]()); // undefined
console.log(foo.bar()); // 1
console.log(arguments[0].call(foo)); // 1
})(foo.bar)
- 三个的
this
分别指向arguments
,foo
,foo
function Foo(){
getName = function(){console.log(1);}
return this;
}
Foo.getName = function(){console.log(2);}
Foo.prototype.getName = function(){console.log(3);}
var getName = function(){console.log(4);}
function getName(){console.log(5);}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
- 也是先把变量提升处理了,改成下面这种,注意:同样是声明提升,但变量提升在函数声明之上
1.打印静态方法,没啥说的
2.全局的getName
一共赋了三次值(两次在全局赋值,一次在局部赋值),第一次:function getName(){console.log(5);}
,第二次:function getName();{console.log(4);}
;第三次为局部赋值:function Foo(){ getName = function(){console.log(1);} return this; }
3.Foo()
构造函数也可以当做普通函数调用,Foo()
调用后,内部又对全局的getName
重新赋值为function(){console.log(1);}
返回的this
指向window
,相当于调用window.getName()
,而此时的getName()
在调用Foo()
后已被赋值为1
4.window.getName()
就等于在全局直接调用getName()
5.不要想太多,反正Foo.getName
就是个普通函数,把Foo.getName
看做abc
就完了,这里只是实例化的过程中伴随了执行的操作罢了
6.相当于实例.getName()
,调用的是原型上的方法Foo.prototype.getName = function(){console.log(3);}
var getName;
function Foo(){
getName = function(){console.log(1);}
return this;
}
function getName(){console.log(5);}
Foo.getName = function(){console.log(2);}
Foo.prototype.getName = function(){console.log(3);}
getName = function(){console.log(4);}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
var fullName = 'language';
var obj = {
fullName:'javascript',
prop:{
getFullName:function(){
return this.fullName;
}
}
}
console.log(obj.prop.getFullName()); // undefined
var test = obj.prop.getFullName;
console.log(test()); // language
- 第一个
this
指向的是obj.prop
- 第二个打印可改成如下,
this
指向window
var fullName = 'language';
var obj = {
fullName:'javascript',
prop:{
getFullName:function(){
return this.fullName;
}
}
}
console.log(obj.prop.getFullName()); // undefined
var test = function(){
return this.fullName
}
console.log(test());
var name = 'Jerry';
var Tom = {
name:'Tom',
show:function(){
console.log(this.name)
},
wait:function(){
var fun = this.show;
fun();
}
};
Tom.wait(); // Jerry
- 因为调用
Tom.wait()
时,this
指向Tom
,因此wait
属性中的this.show
实际上就是Tom.show
,那就可以改成下面这种:
var name = 'Jerry';
var Tom = {
name:'Tom',
show:function(){
console.log(this.name)
},
wait:function(){
function fun(){
console.log(this.name) // 这里的 this 指向 window
}
fun();
}
};
Tom.wait(); // Jerry
setTimeout(function() { // 定时器1
var end = +new Date();
console.log(end - start)
}, 1000)
setTimeout(function() { // 定时器2
console.log('哈哈哈')
}, 0)
console.time('circle')
let delay = +new Date();
while(+new Date() - delay < 1500);
console.timeEnd('circle')
var start = +new Date();

- 分析:
1.想像任务队列是个时间轴
2.定时器1:异步任务,放到任务队列 1000ms 的时间点上。
3.定时器2:异步任务,放到任务队列 0ms 的时间点上
4.同步阻塞 1500ms ,此时,定时器1 的任务也已经从 1000ms 的时间点移至紧随 定时器2 的时间点,也就是说,此时如果 定时器2 执行,定时器1 也将紧随其后执行,不需再等待 1000ms。
5.执行同步任务var start = +new Date();
执行完后,执行栈已空,可以开始执行任务队列中的任务,先执行定时器2,然后 立刻 执行定时器1
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
-
JavaScript 采用词法作用域(静态作用域),函数的作用域在定义时已经确定,
foo
的定义时的value
为全局变量 - 函数定义时确定作用域,函数调用时先创建执行上下文,在具体的执行过程中再进行执行上下文初始化
实现 bind 方法
bind / call 的区别
-
bind
返回函数,call
立即调用
var obj = {
name : 24
}
function fn(){
console.log(this.name);
}
fn.bind(obj)() // 24
fn.call(obj) // 24
new 操作符做了什么
1.创建一个新对象
2.将构造函数的 this 指向新对象
3.执行构造函数
4.返回新对象
function Foo(){
this.name = '我亦飘零';
};
var obj = {};
obj.__proto__ = Foo.prototype;
Foo.call(obj)
函数柯里化
常见的浏览器兼容问题 js / css
* png24位的图片在iE6浏览器上出现背景,解决方案是做成PNG8.也可以引用一段脚本处理.
* 浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。
* IE6双边距bug:块属性标签float后,又有横行的margin情况下,在ie6显示margin比设置的大。
* 浮动ie产生的双倍距离(IE6双边距问题:在IE6下,如果对元素设置了浮动,同时又设置了margin-left或margin-right,margin值会加倍。)
#box{ float:left; width:10px; margin:0 0 0 100px;}
这种情况之下IE会产生20px的距离,解决方案是在float的标签样式控制中加入 ——_display:inline;将其转化为行内属性。(_这个符号只有ie6会识别)
* 渐进识别的方式,从总体中逐渐排除局部。
首先,巧妙的使用“\9”这一标记,将IE游览器从所有情况中分离出来。
接着,再次使用“+”将IE8和IE7、IE6分离开来,这样IE8已经独立识别。
css
.bb{
background-color:#f1ee18;/*所有识别*/
.background-color:#00deff\9; /*IE6、7、8识别*/
+background-color:#a200ff;/*IE6、7识别*/
_background-color:#1e0bd1;/*IE6识别*/
}
* IE下,可以使用获取常规属性的方法来获取自定义属性,
也可以使用getAttribute()获取自定义属性;
Firefox下,只能使用getAttribute()获取自定义属性.
解决方法:统一通过getAttribute()获取自定义属性.
* IE下,event对象有x,y属性,但是没有pageX,pageY属性;
Firefox下,event对象有pageX,pageY属性,但是没有x,y属性.
* 解决方法:(条件注释)缺点是在IE浏览器下可能会增加额外的HTTP请求数。
* Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.
* 超链接访问过后hover样式就不出现了 被点击访问过的超链接样式不在具有hover和active了解决方法是改变CSS属性的排列顺序:
L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
* 怪异模式问题:漏写DTD声明,Firefox仍然会按照标准模式来解析网页,但在IE中会触发怪异模式。为避免怪异模式给我们带来不必要的麻烦,最好养成书写DTD声明的好习惯。现在可以使用[html5](http://www.w3.org/TR/html5/single-page.html)推荐的写法:`<doctype html>`
* 上下margin重合问题
ie和ff都存在,相邻的两个div的margin-left和margin-right不会重合,但是margin-top和margin-bottom却会发生重合。
解决方法,养成良好的代码编写习惯,同时采用margin-top或者同时采用margin-bottom。
* ie6对png图片格式支持不好(引用一段脚本处理)
常用的自适应解决方案
cookie,localStorage,sessionStorage 差异及使用场景
发布 - 订阅模式
- ABC 收藏了某店铺,店铺更新商品时,ABC 就会收到通知。ABC 就是订阅者,店铺就是发布者
多行文本垂直居中
- 父级高度不固定,即内容撑开父级时:为父级添加 padding 即可
- 父级高度固定(IE8 及以上有效):
1.父级添加:display:table;
2.子集添加:display:table-cell; vertical-align:middle
// css
div {
height:500px;
width:300px;
border:1px solid red;
font-size: 30px;
display: table;
}
p {
display: table-cell;
vertical-align: middle;
}
// html
<div>
<p id="">
啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦
</p>
</div>
未知大小图片水平垂直居中
-
<img>
标签外套一层<a>
标签
a{display:inline-block; width:1.2em; font-size:128px; text-align:center; vertical-align:middle;}
img{vertical-align:middle;}
溢出显示省略号
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
伪类 / 伪元素
- 伪类:
属性 | 描述 | CSS |
---|---|---|
:active | 向被激活的元素添加样式。 | 1 |
:focus | 向拥有键盘输入焦点的元素添加样式。 | 2 |
:hover | 当鼠标悬浮在元素上方时,向元素添加样式。 | 1 |
:link | 向未被访问的链接添加样式。 | 1 |
:visited | 向已被访问的链接添加样式。 | 1 |
:first-child | 向元素的第一个子元素添加样式。 | 2 |
:lang | 向带有指定 lang 属性的元素添加样式。 | 2 |
- 伪元素:
属性 | 描述 | CSS |
---|---|---|
:first-letter | 向文本的第一个字母添加特殊样式。 | 1 |
:first-line | 向文本的首行添加特殊样式。 | 1 |
:before | 在元素之前添加内容。 | 2 |
:after | 在元素之后添加内容。 | 2 |
去掉 ul 前面的点
list-style:none
引用的阿里矢量图为什么能改颜色
- 通过伪元素添加的
content
内容,属于文本
矢量图是伪元素的 content
BOM

关于 Vue 双向绑定 和 虚拟DOM
-
实例化 MVVM 的过程中做了以下三件事:
1. 传入一个 options 配置对象,类似这种,MVVM 的参数就是:
2. 实例化 Observer ,用于重写 options.data 所有属性的访问器属性,即 get 和 set 方法,重写的目的是:var vm = new MVVM({ el: '#mvvm-app', data: { someStr: 'hello ', className: 'btn', htmlStr: '<span style="color: #f00;">red</span>', child: { someStr: 'World !' } }, computed: { getHelloWord: function() { return this.someStr + this.child.someStr; } }, methods: { clickBtn: function(e) { var randomStrArr = ['childOne', 'childTwo', 'childThree']; this.child.someStr = randomStrArr[parseInt(Math.random() * 3)]; } } });
a. 通过重写的get
实现:哪里读取数据,就把哪里当做一个Watcher
,并将它存到dep.subs
中
b. 通过重写的set
实现:修改data
数据时调用notice()
通知所有观察者进行更新update()
3. 实例化 Compile , 用于编译 options.el ,效果是:
a. 找出options.el
中引用到options.data
的部分,每找到一处就实例化一个观察者Watcher
,而Watcher
本身定义有一个update()
方法
b. 初始化渲染,初始化时会读取options.data
的数据用于渲染视图,也就将所有Watcher
存入了dep.subs
-
虚拟DOM
将#mvvm-app
内的节点存为一个 文档片段,通过判断模板{{}}
和各种v-
指令,将data
信息写到 文档片段 上,最后将 文档片段 一次插入#mvvm-app
中
几个常用的正则
- 手机号:以 1 开头,第二位是 3~8 中的一个,之后是 9 个 0~9 结尾
/^1[34578][0-9]{9}$/
- 日期匹配:前四位是数字,接着是
/
和-
中的一个,然后两个数字,接着是/
和-
中的一个,然后两个数字结尾
/^\d{4}[/-]\d{2}[/-]\d{2}$/
// 1983-12-27
// 1983/12/16
- 邮箱:第一位是数字字母下划线 + 数字字母下划线横线任意个 + @ + 数字字母至少一个 + (点 + 至少一个字母)至少一组结尾
/^[a-zA-Z0-9_][a-zA-Z0-9_-]*@[a-zA-Z0-9]+(\.[a-zA-Z]+)+$/
- 练习:匹配
aabc222babc222bbabc
中的字母部分
/((a)|(b{1,2}))abc/g
Vue 中 data 为什么使用 return
- 不使用 return 包裹的数据会在项目的全局可见,会造成变量污染
- 使用return 包裹后数据中变量只在当前组件中生效,不会影响其他组件。
- https://cn.vuejs.org/v2/guide/components.html#data-%E5%BF%85%E9%A1%BB%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0
Vue 中使用 data 中的一个数组属性渲染页面,修改数组属性时,页面是否响应
oninput / onchange
- oninput:输入框内容变了就触发
- onchange:输入框内容变化并失去焦点时触发
Vue 组件通信
- 父 → 子:
1.prop
2.子组件调用 this.$parent
3.provide / inject - 父调用子方法
1.ref