前端面试题知识点(TT)
2018-12-07 本文已影响0人
董董董董董董董董董大笨蛋
1,arguments
var length = 10;
function fn() {
alert(this.length);
}
var obj = {
length: 5,
method: function (fn) {
fn(); //?
arguments[0](); //?
},
fn: function() {
console.log(this.length);
}
};
obj.method(fn);
obj.fn();
arguments[0](); == arguments.fn();
2,字符串原型
var a = "123";
a.duplicate(); // "123123"
String.prototype.duplicate = () => {
return this + this;
}
null - (-1) // ? 1
3,css line-height区别
line-height:26px;
line-height:1.5;
line-height:150%;
line-height:1.5rem;
4,字符模版
const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
name: 'Zhang',
city: 'FuJian',
});
console.log(result); // My name is Zhang,I'm from FuJian
const sprintf = (str, data) => (
Object.keys(data).reduce((prev, cur) => {
let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
return prev.replace(reg, data[cur]);
}, str);
);
5,节流
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
6,防抖
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
if([]==false){console.log(1)}; //true
if({}==false){console.log(2)};
if([]){console.log(3)} //true
if([1]==[1]){console.log(4)}
var foo={};
var F = function(){};
Object.prototype.a = 'a';
Function.prototype.b='b';
console.log(foo.a) ==>a;
console.log(foo.b) ==>undefined;
console.log(F.a)==>a;
console.log(F.b)==>b;
7,作用域
var num1 = 55;
var num2 = 66;
function f1(num,num1) {
num = 100;
num1 = 100;
num2 = 100;
console.log(num);
console.log(num1);
console.log(num2);
}
f1(num1, num2);
console.log(num);
console.log(num1);
console.log(num2);
8,值类型和引用类型的传递
function Person(name,age,salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
function f2(person) {
person.name = 'ls';
person = new Person('aa',18,10000);
}
var p = new Person('zs',18,20000);
console.log(p.name);
f2(p);
console.log(p.name)
9,原型面试题
function F(){};
Object.prototype.a=function(){
console.log('a')
}
Function.prototype.b=function(){
console.log('b')
}
var f= new F();
f.a();
f.b();
F.a();
F.b();
function A(){}
A.prototype.n=1;
var b = new A();
A.prototype={
n:2,
m:3
}
var c= new A();
console.log(b.n,b.m,c.n,c.m)
10,MVVM
// 观察者 (发布订阅) 观察者 被观察者
class Dep {
constructor(){
this.subs = []; // 存放所有的watcher
}
// 订阅
addSub(watcher){ // 添加 watcher
this.subs.push(watcher);
}
// 发布
notify(){
this.subs.forEach(watcher=>watcher.update());
}
}
// new Watcher
class Watcher{
constructor(vm,expr,cb){
this.vm = vm;
this.expr = expr;
this.cb = cb;
// 默认先存放一个老值
this.oldValue = this.get();
}
get(){ // vm.$data.school vm.$data.school.name
Dep.target = this; // 先把自己放在this上
// 取值 把这个观察者 和数据关联起来
let value = CompileUtil.getVal(this.vm,this.expr);
Dep.target = null; // 不取消 任何值取值 都会添加watcher
return value;
}
update(){ // 更新操作 数据变化后 会调用观察者的update方法
let newVal = CompileUtil.getVal(this.vm,this.expr);
if(newVal !== this.oldValue){
this.cb(newVal);
}
}
}
// vm.$watch(vm,'school.name',(newVal)=>{
// })
class Observer{ // 实现数据劫持功能
constructor(data){
this.observer(data);
}
observer(data){
// 如果是对象才观察
if(data && typeof data == 'object'){
// 如果是对象
for(let key in data){
this.defineReactive(data,key,data[key]);
}
}
}
defineReactive(obj,key,value){
this.observer(value); // school :[watcher,watcher] b:[watcher]
let dep = new Dep() // 给每一个属性 都加上一个具有发布订阅的功能
Object.defineProperty(obj,key,{
get(){
// 创建watcher时 会取到对应的内容,并且把watcher放到了全局上
Dep.target && dep.addSub(Dep.target);
return value;
},
set:(newVal)=>{ // {school:{name:'珠峰'}} school ={}
if(newVal != value){
this.observer(newVal);
value = newVal;
dep.notify();
}
}
});
}
}
// 基类 调度
class Compiler{
constructor(el,vm){
// 判断el属性 是不是一个元素 如果不是元素 那就获取他
this.el = this.isElementNode(el)?el:document.querySelector(el);
// 把当前节点中的元素 获取到 放到内存中
this.vm = vm;
let fragment = this.node2fragment(this.el);
// 把节点中的内容进行替换
// 编译模版 用数据编译
this.compile(fragment);
// 把内容在塞到页面中
this.el.appendChild(fragment);
}
isDirective(attrName){
return attrName.startsWith('v-');
}
// 编译元素的
compileElement(node){
let attributes = node.attributes; // 类数组
[...attributes].forEach(attr=>{ // type="text" v-model="school.name"
let {name,value:expr} = attr; // v-model="school.name"
// 判断是不是指令 //v-
if(this.isDirective(name)){ // v-model v-html v-bind
let [,directive] = name.split('-'); // v-on:click
let [directiveName,eventName] =directive.split(':');
// 需要调用不同的指令来处理
CompileUtil[directiveName](node,expr,this.vm,eventName);
}
})
}
// 编译文本的
compileText(node){ // 判断当前文本节点中内容是否包含 {{xxx}} {{aaa}}
let content = node.textContent;
if(/\{\{(.+?)\}\}/.test(content)){
// 文本节点
CompileUtil['text'](node,content,this.vm); // {{a}} {{b}}
}
}
// 核心的编译方法
compile(node){ // 用来编译内存中的dom节点
let childNodes = node.childNodes;
[...childNodes].forEach(child=>{
if(this.isElementNode(child)){
this.compileElement(child);
// 如果是元素的话 需要把自己传进去 在去遍历子节点
this.compile(child);
}else{
this.compileText(child);
}
});
}
// 把节点移动到内存中
node2fragment(node){
// 创建一个文档碎片
let fragment = document.createDocumentFragment();
let firstChild;
while(firstChild = node.firstChild){
// appendChild具有移动性
fragment.appendChild(firstChild);
}
return fragment
}
isElementNode(node){ // 是不是元素节点
return node.nodeType === 1;
}
}
// 3月16日开班
CompileUtil = {
// 根据表达式取到对应的数据
getVal(vm,expr){ // vm.$data 'school' [school,name]
return expr.split('.').reduce((data,current)=>{
return data[current];
},vm.$data);
},
setValue(vm,expr,value){ //vm.$data 'school.name' = 姜文
expr.split('.').reduce((data,current,index,arr)=>{
if(index == arr.length-1){
return data[current] = value;
}
return data[current];
},vm.$data);
},
// 解析v-model这个指令
model(node,expr,vm){ // node是节点 expr 是表达式 vm是当前实例 school.name vm.$data
// 给输入框赋予value属性 node.value = xxx
let fn = this.updater['modelUpdater'];
new Watcher(vm,expr,(newVal)=>{ // 给输入框加一个观察者 如果稍后数据更新了会触发此方法,会拿新值 给输入框赋予值
fn(node,newVal);
});
node.addEventListener('input',(e)=>{
let value = e.target.value; // 获取用户输入的内容
this.setValue(vm,expr,value);
})
let value = this.getVal(vm,expr); // 珠峰
fn(node,value);
},
html(node,expr,vm){ // v-html="message"
let fn = this.updater['htmlUpdater'];
new Watcher(vm,expr,(newVal)=>{
console.log(newVal)
fn(node,newVal);
});
let value = this.getVal(vm,expr); // 珠峰
fn(node,value);
},
getContentValue(vm,expr){
// 遍历表达式 将内容 重新替换成一个完整的内容 返还回去
return expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
return this.getVal(vm,args[1]);
})
},
on(node,expr,vm,eventName){ // v-on:click="change" expr
node.addEventListener(eventName,(e)=>{
vm[expr].call(vm,e); // this.change
})
},
text(node,expr,vm){ // expr => 珠峰 {{b}} {{c}} => a b
let fn = this.updater['textUpdater'];
let content = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
// 给表达式每{{}} 都加上观察者
new Watcher(vm,args[1],()=>{
fn(node,this.getContentValue(vm,expr)); // 返回了一个全的字符串
});
return this.getVal(vm,args[1]);
});
fn(node,content);
},
updater:{
htmlUpdater(node,value){ // xss攻击
node.innerHTML = value;
},
// 把数据插入到节点中
modelUpdater(node,value){
node.value = value;
},
// 处理文本节点的
textUpdater(node,value){
node.textContent = value;
}
}
}
class Vue{
constructor(options){
// this.$el $data $options
this.$el = options.el;
this.$data = options.data;
let computed = options.computed;
let methods = options.methods;
// 这个根元素 存在 编译模板
if(this.$el){
// 把数据 全部转化成用Object.defineProperty来定义
new Observer(this.$data);
// 把数据获取操作 vm上的取值操作 都代理到 vm.$data
// {{getNewName}} reduce vm.$data.getNeName
for(let key in computed){ // 有依赖关系 数据
Object.defineProperty(this.$data,key,{
get:()=>{
return computed[key].call(this);
}
})
};
for(let key in methods){
Object.defineProperty(this,key,{
get(){
return methods[key]
}
})
}
this.proxyVm(this.$data);
new Compiler(this.$el,this);
}
}
//backbone set() get()
proxyVm(data){
for(let key in data){ // {school:{name,age}}
Object.defineProperty(this,key,{ // 实现可以通过vm取到对应的内容
get(){
return data[key]; // 进行了转化操作
},
set(newVal){ // 设置代理方法
data[key] = newVal;
}
})
}
}
}
11,深拷贝
function copy(obj) {
var vv = obj instanceof Array ? []:{};
for(var i in obj) {
if(typeof obj[i] == 'object'){
vv[i] = copy(obj[i])
}else {
vv[i] = obj[i]
}
}
return vv
}
//考虑null
function copy(obj) {
var vv = null;
if(typeof obj == 'object' && obj !== null) {
vv = obj instanceof Array ? [] : {};
for(var i in obj) {
vv[i] = copy(obj[i])
}
}else {
vv = obj
}
return vv
}
12,函数调用
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)
}
// ouput:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
13,观察者模式
class Subject{
constructor(){
this.subs = [];
}
subscribe(sub) {
this.subs.push(sub)
}
unsubscribe(sub) {
const index = this.subs.indexOf(sub);
if(index > -1){
this.subs.splice(index,1);
}
}
fire() {
this.subs.forEach(sub=>{
sub.notify()
})
}
}
class Observer {
constructor(data){
this.data
}
notify(){
console.log(this.data)
}
}
let subject = new Subject()
let ob1 = new Observer('hello')
let ob2 = new Observer('world')
subject.subscribe(ob1)
subject.subscribe(ob2)
subject.fire()
14,发布订阅模式
class EventChannel {
constructor() {
// 主题
this.subjects = {}
}
hasSubject(subject) {
return this.subjects[subject] ? true : false
}
/**
* 订阅的主题
* @param {String} subject 主题
* @param {Function} callback 订阅者
*/
on(subject, callback) {
if (!this.hasSubject(subject)) {
this.subjects[subject] = []
}
this.subjects[subject].push(callback)
}
/**
* 取消订阅
*/
off(subject, callback) {
if (!this.hasSubject(subject)) {
return
}
const callbackList = this.subjects[subject]
const index = callbackList.indexOf(callback)
if (index > -1) {
callbackList.splice(index, 1)
}
}
/**
* 发布主题
* @param {String} subject 主题
* @param {Argument} data 参数
*/
emit(subject, ...data) {
if (!this.hasSubject(subject)) {
return
}
this.subjects[subject].forEach(callback => {
callback(...data)
})
}
}
const channel = new EventChannel()
channel.on('update', function(data) {
console.log(`update value: ${data}`)
})
channel.emit('update', 123)
15,柯里化函数
柯里化的定义:在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
function curry (fn, currArgs) {
return function() {
let args = [].slice.call(arguments);
// 首次调用时,若未提供最后一个参数currArgs,则不用进行args的拼接
if (currArgs !== undefined) {
args = args.concat(currArgs);
}
// 递归调用
if (args.length < fn.length) {
return curry(fn, args);
}
// 递归出口
return fn.apply(null, args);
}
}
测试一下
柯里化的目的是,减少代码冗余,以及增加代码的可读性
function sum(a, b, c) {
console.log(a + b + c);
}
const fn = curry(sum);
fn(1, 2, 3); // 6
fn(1, 2)(3); // 6
fn(1)(2, 3); // 6
fn(1)(2)(3); // 6
都能输出 6 了,搞定!