美文网首页前端开发那些事让前端飞Web前端之路
【面试向】极简前端知识点(不断更新)

【面试向】极简前端知识点(不断更新)

作者: _月光临海 | 来源:发表于2018-05-16 00:09 被阅读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
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]'
    
  • 判断 NaNisNaN(),或者 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 得知 xPerson,而工厂模式则无法知道 xPerson
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 ,并且不同实例的 arrsay 并不是一个玩意

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 后,floatclearvertical-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 分别指向 argumentsfoofoo

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
BOM & DOM
关于 Vue 双向绑定 和 虚拟DOM
  • 实例化 MVVM 的过程中做了以下三件事:
    1. 传入一个 options 配置对象,类似这种,MVVM 的参数就是:
      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)];
              }
          }
      });
    
    2. 实例化 Observer ,用于重写 options.data 所有属性的访问器属性,即 get 和 set 方法,重写的目的是:
    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


Vue 中使用 data 中的一个数组属性渲染页面,修改数组属性时,页面是否响应


oninput / onchange

  • oninput:输入框内容变了就触发
  • onchange:输入框内容变化并失去焦点时触发

Vue 组件通信

  • 父 → 子:
    1.prop
    2.子组件调用 this.$parent
    3.provide / inject
  • 父调用子方法
    1.ref

相关文章

网友评论

本文标题:【面试向】极简前端知识点(不断更新)

本文链接:https://www.haomeiwen.com/subject/vrcakftx.html