部分知识自查面试题整理,原型部分后为收集资料,未思考总结,后续补上
答: 是 J q u e r y 的 别 名 , 可 以 通 过 是Jquery的别名,可以通过 是Jquery的别名,可以通过生成Jquery对象,调用Jquery的方法
答:从个人角度而言根据css样式选择器进行划分:
1.简单选择器,将css样式的基本选择器如id选择器,class选择器,标签选择器等作为对象参数传入 符 , 获 取 到 所 需 d o m 元 素 , 如 获 取 所 有 的 d i v 标 签 , 符,获取到所需dom元素,如获取所有的div标签, 符,获取到所需dom元素,如获取所有的div标签,(‘div’)
2.层次选择器,根据元素的层次递进关系如div标签下的ul标签下的li标签来确认作为参数传入 符 的 对 象 , 获 取 到 所 需 d o m 元 素 , 如 获 取 所 有 u l 标 签 下 l i 标 签 下 的 a 标 签 , 符的对象,获取到所需dom元素,如获取所有ul标签下li标签下的a标签, 符的对象,获取到所需dom元素,如获取所有ul标签下li标签下的a标签,(‘ul li a’)
3.过滤选择器,基于简单选择器和层次选择器,通过语法类似于css中伪类选择器的过滤规则筛选获取所需到dom元素,如获取li标签中的第一个元素,$(‘li:first’)
答:首先,两者功能都是等待页面加载完后执行指定函数,但是Jquery ready函数是在页面dom结构加载完后即执行,而onload事件则需等待所有文件包括图片、音频、视频等外部资源加载完成后执行,其次,onload事件只能执行一次,执行第二次会将第一次的执行结果覆盖,而jquery的ready函数则可以执行多次且不会覆盖上一次的执行结果。
答:$(’[name=NameOfSelectedTag] :selected’)
答:$(this)获取的是一个Jquery对象,可以调用Jquery方法,this指向所需元素,不能调用Jquery方法
答:是一个用于快速开发的前端框架,使用的原因是移动设备优先,响应式开发,操作简单等。
答:html5,也就是Doctype文档,因为bootstrap使用了部分的h5的属性和css样式,如果文档声明类型不一致,可能出现浏览器显示与预期效果不同。
答:
渐进增强是先实现基础功能,随后根据各个浏览器不断增加功能,优化实现效果,简单来说,就是基于浏览器低版本,先达到最低要求,随后向上迭代
优雅降级则是先完善所有功能,随后针对各个浏览器进行测试,修复漏洞,确保低版本浏览器也拥有最基本的功能,简单来说就是基于浏览器高版本,实现高要求,随后向下迭代
答:number,string,boolean,symbol,undefined,null,object
var obj1 = { x: 5 }; var obj2 = obj1; obj1.a = obj1 = { x: 6 }; console.log(obj1.a); console.log(obj2.a); //求打印结果
答:打印结果为:undefined和{x : 6}
答:
function cloneobj(source, target = {}) { let classList = [Set, Map, Date, RegExp]; let keys = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source)); for (let i = 0; i < keys.length; i++) { if (keys[i] === "prototype") continue; let desc = Object.getOwnPropertyDescriptor(source, keys[i]); if (desc.value instanceof Object) { let o; if (desc.value instanceof HTMLElement) o = desc.value.cloneNode(true); else if (classList.includes(desc.value.constructor)) o = new desc.value.constructor(desc.value); else if (desc.value.constructor === Function) { let arr = desc.value.toString().replace(/\n/g, "").match(/function\s*\((.*?)\)\s*\{(.*?)\}/).slice(1); o = new Function(arr[0], arr[1]) } else o = new desc.value.constructor(); Object.defineProperty(target, keys[i], { value: o, enumerable: desc.enumerable, writable: desc.writable, configurable: desc.configurable }) cloneobj(desc.value, o); } else Object.defineProperty(target, keys[i], desc); } return target; }
答:首先,call方法传入的参数数量不定,第一个参数为this指定对象,如果为null或者undefined,则指向window,其余参数则传入函数,根据需求调用,其次,apply方法传入参数只有两个,第一个与call相同,为this指定对象,第二个为类数组对象,传入函数,bind则是改变this指向的同时,传入参数,返回一个新函数,该函数会在调用时才执行;就性能而言,call性能优于apply,传入参数3个以内时两者差距不大,传入参数超过三个,则call方法性能优于apply。
答:首先,call传入参数数量不定,apply传入参数只能有两个,其次,apply方法传入的第二个参数必须为数组或者类数组对象,而bind函数在改变this指向的同时会返回的是一个新函数,需要重新调用才会执行。
let a=0; const obj={ a:1, b:function () { console.log(this.a); }, }; const obj1={ a:2 } const fun=obj.b; fun(); fun.apply(obj); fun.bind(obj1).call(obj); const fun1=fun.bind(obj1) new fun() //求打印结果
答:打印结果为:fun() 打印undefined,fun.apply(obj)打印1,fun.bind(obj1).call(obj) 打印2,new fun() 打印undefined
var myObject = { foo: "bar", func: function() { var self = this; console.log("outer func: this.foo = " + this.foo); console.log("outer func: self.foo = " + self.foo); (function() { console.log("inner func: this.foo = " + this.foo); console.log("inner func: self.foo = " + self.foo); }()); } }; myObject.func(); //inner func: this.foo = bar //inner func: self.foo = bar //inner func: this.foo = undefined //inner func: self.foo = bar
var length = 10; function fn() { console.log(this.length); } var obj = { length: 5, method: function(fn) { fn(); arguments[0](); } }; obj.method(fn, 1); // 10 2
window.number = 2; var obj = { 'number': 3, 'db1': (function(){ console.log(this); this.number *= 4; return function(){ console.log(this); this.number *= 5; } })() } var db1 = obj.db1; db1(); obj.db1(); console.log(obj.number); //打印window //window.number=2*4 //obj.db1= function () {console.log(this);this.number *= 5;} //db1=function () {console.log(this);this.number *= 5;} //打印window //window.number=2*4*5 //打印obj //obj.number = 3*5 //打印15
var name = '222' var a = { name: '111', say: function () { console.log(this.name) } } var b = { name: '333', say: function (fn) { fn() } } a.say() b.say(a.say) //打印111 //打印222
var obj = { a: 10, c: 50, b: { a: 0, c: this.a, run: function () { console.log(this.c); } } } //this.c→obj.b.c,this.a→window.a //打印undefined
笔记:判断this:
函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。
var bar = new foo()
函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。 var bar = foo.call(obj2)
函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。
var bar = obj1.foo()
如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。
答:js对象中的访问器属性是指的一组获取和设置值的函数,setter和getter,getter获取值后返回,setter设置值,没有返回值,访问器属性无法直接访问,可以通过Object.getOwnPropertyDescriptor()方法获取,但是与value值冲突,访问器属性不能与value属性共存
答:闭包就是一个函数和一个函数内部可以访问到的外部的局部变量的集合,闭包是js函数作用域的副产品,简单来说,js的函数内部可以调用函数外部的局部变量时,就产生了闭包。
答:可以实现外部访问函数内部的变量,可以保存函数内的局部变量,可以防止全局变量被污染,即公共属性和私有属性模块化。
答:实际闭包并没有造成内存泄漏,证明:闭包的变量可以访问使用,而非占据着内存空间却又无法调用的孤儿对象,觉得闭包造成内存泄漏的原因是因为ie浏览器本身存在bug,在使用完闭包后,依然回收不了闭包中引用的变量。闭包内变量是否回收决定于:
1.嵌套函数中是否使用该变量
2.嵌套函数中是否存在直接调用eval方法
3.是否使用了with表达式
详细证明
答:一种转换函数的技术,将接受多个参数的函数转换为接受单个参数的函数并且该函数的返回值是一个可以接受其余参数的新函数。
答:调用函数时,如果存在每次调用都相同的参数,可以避免重复传入该参数,比如比较两个变量a1,a2是否都是a,正常来说每次都要传入参数a,但是通过柯里化可以固定参数a,只传入单个参数即可比较。
答:实现一个对象调用本不属于他本身的方法,比如,可以通过函数反柯里化实现一个nodelist调用数组的forEach方法实现需求。
答:
function curry(fn){ let arr = []; return function(){ if(arguments.length > 0){ [].push.apply(arr,arguments); return arguments.callee; } else return fn(...arr); } } //反柯里化 此处为笔记,不是答案 Function.prototype.unCurry=function(){ var self=this; return function(thisArg,...arg){ return self.apply(thisArg,arg); } }
答:解析1,解析2
答:解析1,解析2
答:解析1,解析2
function Foo() { Foo.a = function() { console.log(1) } this.a = function() { console.log(2) } } Foo.prototype.a = function() { console.log(3) } Foo.a = function() { console.log(4) } Foo.a(); let obj = new Foo(); obj.a(); Foo.a(); /* 解析: Foo.a() 的输出值为 4。此时并没有调用 Foo 构造函数。 obj.a() 的输出值为 2。虽然原型上也有 a 方法。但是实例对象 obj 上也有 a 方法,所以会先在自身找,找不到时才去原型链上找。 Foo.a() 的输出值为1。调用了构造函数 Foo 之后,Foo 内部的 this.a 方法覆盖了外层的 Foo.a 。 */
//写出以下所有实例对象的原型链 function Person(name){ this.name = name; }; var jack = new Person("jack"); /* 解析: 实例对象的原型链: jack.__proto__ === Person.prototype; Person.prototype.__proto__ === Object.prototype; jack.__proto__.__proto__ === Object.prototype; 构造函数的原型链: Person.__proto__ === Function.prototype; Function.prototype === Function.__proto__; Function.prototype.__proto__ === Object.prototype; Function.__proto__.__proto__ === Object.prototype; Person.__proto__.__proto__ === Object.prototype; */
答:
// ES5 的面向对象 // console.log(Components)//能拿到返回的Components函数,也就意味着可以在Components里写入各种原型内容 var elem = new Components("div") elem.appendTo("body") console.log(elem)//里面的方法都不可枚举 for(var prop in elem){ console.log(prop)//拿不到任何数据 因为elem中的属性方法都不能被拿到 } // ES5 的继承 function Box(type){ //Box继承Components 因为ES5没有extends 对象继承方法,所以就要考虑继承实际上是Box函数 的原型链当中有Components // super(type)// 正常情况下,这个type要传给 Components 所以 要执行Components // 这是 第二种 继承方式 用到的 // Components.call(this,type);//用当前Box 中的this 替代Components中的this,当前Box的this 就是实例化对象 // 最终的继承方式 // console.log(this)//this下面的[[Prototype]] 有 superClass属性 // console.log(this.superClass)//这个就是 Components返回的 Component原型 // this.superClass.constructor.apply(this,arguments);//把超类的构造函数做apply,然后用Box的实例化对象(this)替换超类的构造函数中的this,然后把Box中的参数也带进去,这样就完成了 构造函数的创建 // 这样写的好处 就是 不用考虑 超类是谁,这种写法更加的通用,类似于 ES6中的super() // 但是这样写 过于 繁琐,当我们在Fuction原型中 设置一个super时,发现this就是当前Box函数本身,所以可以把这句话 写到Fuction原型当中 this.super(type)//传入参数 } // var a = new Box(); // var c = new Components(); // 如果Box 继承 Components 那么 a.__proto__ = c ,这样才是继承 // a.__proto__ = c; // console.log(a)//对比ES6 发现 原型第一层里面会少一个constructor,因为这里 a.__proto__ = c; 直接把c赋值给c, 而a.__proto__本身会有 constructor ,当把c赋值后,就意味着把 a.__proto__ 原本的内容覆盖了,constructor就会没有了,而且 __proto__ 不允许被我们直接修改 // 第一种 ------继承方法 直接设置原型链 // 1、直接设置原型链,没有后遗症,缺点是不兼容IE11以下 // Object.setPrototypeOf(Box.prototype,Components.prototype); // 当我们 用Object.setPrototypeOf()方法时 // Object.setPrototypeOf(a,c) // console.log(a)//对比发现 还是缺少 constructor ,如果需要再给a 重新添加一个constructor,也就意味着 每次new Box()时,都需要设置 constructor ,不合理 所以应该考虑 给类的本身 写一个 constructor,不考虑给实例化对象设置, 类的原型 函数的原型 等同于实例的__proto__ 原型链 // Object.setPrototypeOf(Box.prototype,Components.prototype); //这里设置后 就有了 constructor ,这样就意味着继承了 // var a = new Box(); // console.log(a) // Object.setPrototypeOf() 将会消耗性能 所以要尽量避免使用这个方法,并且 Object.setPrototypeOf 需要IE11以上才能使用,IE8,IE9根本无法使用 ,写ES5的目的就是为了兼容 // 第二种 ------继承方法 通过new Components() 然后自己手动添加 constructor // 为了达到 Box的原型中有 Components ,可以设置Box的原型是c // Box.prototype = c; // var a = new Box(); // console.log(a)//对比发现 Box第一层原型还是 没有constructor ,因为这里是把 Components的实例化对象 赋值给了 Box.prototype,所以没有constructor ,如果想有的话 还需要写一个constructor 属性 // 使用 Object.assign // Object.assign(Box.prototype,c) // var a = new Box(); // console.log(a)//-----------这种方法是错误的 因为__proto__属于不可枚举属性 // 使用 // Box.prototype = new Components();//Box.prototype 指向 Components实例化对象 // Object.defineProperty(Box.prototype,"constructor",{value:Box})//给Box 设置一个constructor 属性 值为Box // var a = new Box(); // console.log(a) //这种可以实现 他的兼容是IE9 及以上 兼容性不好 所以直接手动添加 constructor // 手动添加 constructor 这样会暴露constructor 但是只能这样了 // Box.prototype = new Components(); // Box.prototype.constructor = Box; // var a = new Box(); // console.log(a) // 这样做 看似没有问题,但是实际上 有隐藏风险,当执行 Components()时,Components里面自带一个type,所以new Box()放入一个div // Box.prototype = new Components(); //这里首先会 执行一次Components() 并且这里没有传参,传参要在new Box传参,所以这里这样写 显然是不行的 // Box.prototype.constructor = Box; // var a = new Box("div");//放入参数 // a.appendTo("body")//当把创建的元素放到body中 就会 得到parent属性 // console.log(a) // 总结: 可以完成兼容低版本浏览器,但是被继承的构造函数,在没有super时已经被执行一次了,缺陷是父类构造函数被执行两次 // 所以要解决 被执行两次的问题,所以要做个处理,因为new Components()就要执行一次构造函数,无非是new Components()是为了拥有和Components一样的 原型链 // Box.prototype = Components.prototype //这样写不好因为他们从继承关系变成了兄弟关系 // 第三种 ------继承方法 利用组合寄生式继承 ,new Components()时 执行另外一个函数 // 那么可以 不执行 Components,执行另外一个函数 // function Components1(){ // } // Components1.prototype = Components.prototype;//Components1和Component 是兄弟关系 // var s = new Components1(); // console.log(s)//此时 Components1 的原型 就是 Components的原型,当new Components1()时,并没有执行 Component函数,执行的是Components1的函数,也就意味着可以跳过 Component函数,也就意味着 按照上面的写法 把这个Components1 夹进去就可以了 // function Components1(){ // } // Components1.prototype = Components.prototype;//把Components的原型 寄生到 Components1上 // Box.prototype = new Components1();//这里没有执行 Component 执行的是 Components1的 // Box.prototype.constructor = Box;//添加构造函数 // var a = new Box("div");//放入参数 // a.appendTo("body") // console.log(a)// Components 仅执行 一次 并且效果和ES6 一直,除了 constructor 是可枚举类型 // 这里实际上是把Components1 作为了中间的对象,作为中间的类 放置进去 完成了兄弟类 // 总结 : 组合寄生式继承,缺点是麻烦,需要包装,如果Box类已经有一些原型方法和属性,会被覆盖,因为Box.prototype = new Components1() 把Box原有的属性覆盖了,这里需要重新处理 // 最终的继承方式 ----包装后的继承 // 想的是让Box 去继承某个类 ,也就希望Box.extends(Components); 这样一写就能直接继承,Box是一个函数,Box.extends也就是给函数增加一个方法,所以就给任何函数增加extends方法 在Function原型链上设置 Function.prototype.extends = function(supClass){ //设置 函数原型链 supClass就是父类超类 // 这里的 this 就是子类 在这里指的是Box supClass就是Components function F(){} F.prototype = supClass.prototype; // 为了不让Box类一些已有的原型方法和属性丢失,所以先把 里面的东西都拷贝出来,但是有个问题,原来的原型下面的方法 可能是用Object.defineProperty实现的,有不可枚举的属性,为了获取所有的方法,如果用 Object.getOwnPropertyNames 这种方法 ,但是这个 是兼容IE9及以上的,所以不能使用这个,如果不想兼容IE8可以直接用这个方法 /* var o = {} for(var porp in this.prototype){//遍历当前Box函数原型下的所有属性,然后保存到另一个对象 o 中 o[prop] = this.prototype[prop] //保存 可枚举属性 } // 不可枚举属性 其实只有一个属性 constructor ,所以 constructor 后面给他增加就好了 this.prototype = new F();//覆盖 for (var key in o){// 覆盖完成后 把原来保存的东西 拿回来 this.prototype[key] = o[key];//把原来有的东西在复制回来 } */ // 也可以 直接 把 this.prototype 赋值给o ,因为下面的this.prototype = new F(), this.prototype等于了新对象,这时候 o 和 现在的this.prototype没有引用关系,也就意味着 o可以赋值给现在的this.prototype var o = this.prototype; this.prototype = new F(); for(var porp in this.prototype){ o[prop] = this.prototype[prop] } this.prototype = new F(); for (var key in o){ this.prototype[key] = o[key]; } // 给 this.prototype 增加不可枚举的方法 this.prototype.constructor = this;//这样就有了constructor内容 // 有时候可能 supClass的 constructor 也不等于 supClass的 那么就设置让他等于 if(supClass.prototype.constructor!==supClass){ supClass.prototype.constructor=supClass; } // this.prototype.superClass = supClass.prototype //为了以后可能别的地方还用到 这个父类的原型,所以就设置一个superClass属性 // 多的这个 superClass 下面有constructor 这是 Component this.prototype.super = function(){//super方法 把 要带入的参数 放进去,要带的参数,就是Box中的arguments // console.log(this)//这里的this 是 Box 就是 创建的函数 // this.superClass.constructor.apply(this,arguments); //所以在Box中定义的那句话 可以直接写到这里面 supClass.prototype.constructor.apply(this,arguments);//这样的话 可以把上面 定义的supClass.prototype 进行简写 直接拿过来用 } this.prototype.superClass = supClass.prototype this.prototype.superFn = function(fnName){//fnName Box函数要用的继承类上的方法名 // console.log(this)//this就是Box的实例化对象 var arr = [].slice.call(arguments).slice(1);//取到传过来的参数 if(supClass.prototype[fnName]) supClass.prototype[fnName].apply(this,arr)//继承的父类有这个方法名 那么就用this.superClass.appendTo.apply(this,arguments) 类似这种方法 } } Box.extends(Components); // 继承以后 如果需要重构父类的方法比如 appendTo方法 Box.prototype.appendTo = function(parent){ // console.log(parent) // 重构时 我们希望先执行超类的方法,然后再执行 我自身的内容 在ES6中是用 super.appenTo()来完成 // super.appendTo(parent);//希望也和ES6 一样这种来执行 // console.log(this) //Box的实例化对象 // console.log(this.superClass)//能拿到原型上面的方法,所以可以直接继承父类的方法 // this.superClass.appendTo.apply(this,arguments) //就会执行内容,这样就完成了继承 把创建的元素 放到了指定的里面 ,但是这样写法 有点繁琐 可以把appendTo抽离出来,当成一个方法,利用反柯里化,也很繁琐,所以在 Function原型上定义一个superFn this.superFn("appendTo",parent)//带入 弄那个方法的方法名 } var a = new Box("div"); a.appendTo("body"); console.log(a) // ES6 继承 class Component { elem; parent; static cssBool = false; constructor(type = "div") { this.elem = document.createElement(type); } // 如果静态方法需要继承,并且静态属性需要也被各自使用,那么这个属性就必须使用this调用 static setCss(str = "") { if (this.cssBool) return true; this.cssBool = true; // Utils.setCss(str); return false; } appendTo(parent) { if (typeof parent === "string") parent = document.querySelector(parent); parent.appendChild(this.elem); this.parent = parent; } insertTo(parent, before = undefined) { if (typeof parent === "string") parent = document.querySelector(parent); if (typeof before === "string") before = document.querySelector(before); if (!before) parent.appendChild(this.elem); else parent.insertBefore(this.elem, before); this.parent = parent; } remove() { this.elem.remove(); this.elem = null; } } class Ball extends Component { //Ball 类继承 Components constructor() { super(); } appendTo(parent) { console.log(parent) super.appendTo(parent) } } let b = new Ball(); b.appendTo("body") console.log(b); // 1、直接设置原型链,没有后遗症,缺点是不兼容IE11以下 // Object.setPrototypeOf(Box.prototype,Component.prototype); // 2、可以完成兼容低版本浏览器,但是被继承的构造函数,在没有super时已经被执行一次了,缺陷是父类构造函数被执行两次 // Box.prototype=new Component(); // Box.prototype.constructor=Box; // 3、组合寄生式继承,缺点是麻烦,需要包装,如果Box类已经有一些原型方法和属性,会被覆盖 // function F(){} // F.prototype=Component.prototype; // Box.prototype=new F(); // Box.prototype.constructor=Box; function Box(type){ this.super(type); } // 包装后的继承方法 Function.prototype.extends=function(supClass){ function F(){} F.prototype=supClass.prototype; var o=this.prototype; this.prototype=new F(); for(var key in o){ this.prototype[key]=o[key]; } this.prototype.constructor=this; if(supClass.prototype.constructor!==supClass){ supClass.prototype.constructor=supClass; } this.prototype.super=function(){ supClass.prototype.constructor.apply(this,arguments); } this.prototype.superFn=function(fnName){ var arr=[].slice.call(arguments).slice(1); if(supClass.prototype[fnName]) supClass.prototype[fnName].apply(this,arr) } } Box.extends(Component); Box.prototype.appendTo=function(parent){ this.superFn("appendTo",parent); } var a=new Box("div"); a.appendTo("body"); console.log(a); // console.log(a);
//commonjs // 这里的 ES5类和 ES6不一样 先不上来就继承EventTarget // 对于本身来说 ES5当中,因为还没有学模块化,而ES5本身自身 又不能模块化,模块化就是一个文件 一个内容,这里ES5 不能这样,ES5所有内容都会放到文件里面,加载进入的,也就是通过script 加载进来的,或者加载别的文件通过别的方式加载,比如用AJAX加载进来,不管怎么说 都是加载进来运行的,合在一起运行的,不是导入导出这种方式,所以这个一定 是一个全局的变量 // ES5 的面向对象 实例化方法写在原型对象当中,静态方法写在类本身 函数本身的方法当中 var Components=(function(){//设置一个变量叫Components 在ES5面向对象中,类就是构造函数,所以这里写了一个自执行函数,自执行函数中写了一个function Component函数 ,然后 把这个函数返回出去,当return出去以后 也就意味着 外面的Components是一个函数 就是这个返回的函数 function Component(type){//ES6 中可以写type="div" 默认值,但是es5没有等号写法 // console.log(type) if(type===undefined) type="div";//type如果没有赋值 就默认div if(typeof type!=="string") throw new Error("Create Elements Error!");//规定type的类型 必须为string否则就抛错 Object.defineProperty(this,"elem",{writable:true,value:document.createElement(type)}); // this,"elem" 指的是 给this对象下创建一个elem属性,也就是定义了 this.elem //这里的this指的是实例化对象,并且要把elem 隐藏,不可 枚举 } Object.defineProperties(Component.prototype,{//为了不让别人看到代码当中、原型当中的所有内容,也就意味着不让它 有 可枚举 可删除 可修改,因为它是一个基类,基础类型,那么就设置定义 Component.prototype 原型,这个原型 是个对象 ,所以可以新增方法和属性 // elem:{value:null,writable:true}, //先新增一个elem属性,先把elem 的value设置为null为空,然后可以进行修改,但是不能枚举 // parent:{value:null,writable:true},//parent和elem 设置一样 appendTo:{writable:true,value:function(parent){//可传参 ,下面都是value 中这个函数的内容 if(typeof parent==="string") parent=document.querySelector(parent); parent.appendChild(this.elem); Object.defineProperty(this,"parent",{writable:true,value:parent});//给this中增加一个parent属性 他的结果是parent }}, insertTo:{writable:true,value:function(parent,before){//可传参 ,下面都是value 中这个函数的内容 if(typeof parent==="string") parent=document.querySelector(parent); if(typeof before==="string") before=document.querySelector(before); if(!before) parent.appendChild(this.elem); else parent.insertBefore(this.elem,before); Object.defineProperty(this,"parent",{writable:true,value:parent}); }}, remove:{writable:true,value:function(){//可传参 ,下面都是value 中这个函数的内容 this.elem.remove(); this.elem=null; }} }) Object.defineProperties(Component,{//ES6中的 静态属性、静态方法 ,ES5中 就是给函数自身 增加一个属性 cssBool:{value:false,writable:true},//仅可修改 不可枚举 setCss:{value:function(str){//静态方法 if(this.cssBool) return true; if(str===undefined) str=""//如果str 没有写 str就为空字符串 this.cssBool=true; Utils.setCss(str);//Utils没有 所以这里没有办法载入,只能通过页面引入 return false; }} }) /* 第二种写法 网上大多数这么写 但是这样有个弊端 ,当用for in 便利时 下面所有的方法 都能被枚举遍历*/ // Component.prototype.appendTo = function(parent){ // if(typeof parent==="string") parent=document.querySelector(parent); // parent.appendChild(this.elem); // this.parent=parent; // } // Component.prototype.insertTo = function(parent,before){ // if(typeof parent==="string") parent=document.querySelector(parent); // if(typeof before==="string") before=document.querySelector(before); // if(!before) parent.appendChild(this.elem); // else parent.insertBefore(this.elem,before); // this.parent=parent; // } // Component.prototype.remove = function(){ // this.elem.remove(); // this.elem=null; // } // Component.prototype.setCss = function(str){ // if(this.cssBool) return true; // this.cssBool=true; // Utils.setCss(str); // return false; // } return Component; })()
答:
commonjs是通过module.exports导出模块,用require引入一个模块,原理:闭包
requirejs是通过define定义导出模块,用require引入模块。
define('name',[],function(){ return 'requirejs' }) define('say',['name'].function(name){ return "my name is" + name }) require(['say'],function(text){ console.log(text) })
答:
CommonJS导出模块的方法是exports,导入模块的是require,具体规范:
1)如果一个JS文件中存在exports或require,该JS文件是一个模块
2)模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
3)如果一个模块需要暴露一些API提供给外部使用,需要通过exports导出,exports是一个空的对象,你可以为该对象添加任何需要导出的内容
4)如果一个模块需要导入其他模块,通过require实现,require是一个函数,传入模块的路径即可返回该模块导出的整个内容
AMD
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
AMD 推崇依赖前置。
CMD
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
CMD 推崇依赖就近
答:设计模式是一套反复使用的并且经过分类编目的代码设计经验总结。
答:
GOF提出的23种设计模式,分为三大类。
创建型模式,共5种,分别是工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共7种,分别是适配器模式、装饰器模式、代理模式、外观模式桥接模式、组合模式、享元模式。
行为型模式,共11种,分别是策略模式、模板方法模式、观察者模式、选代子模式、責任链模式、命令模式、备忘录模弌、状态模式、访问者模式、中介者模式、解释器模式。
答:工厂模式需要3个基本步骤,原料投入、加工过程以及成品出厂,
优点:
(1)一个调用者想创建一个对象,只要知道它的名称即可。
(2)扩展性高,如果想增加一个产品,只要扩展一个工厂类即可。
(3)屏蔽产品的具体实现,调用者只需关心产品的接口。
缺点:
每次增加一个产品时,都需要増加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定 程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
答:
单例模式用于公共数据存储,与中介者交互双方数据,在某些情况下,在处理多个模块的数据时,他们没有什么联系,但是我们想要用一个公共的部分来进行储存状态或者数据。
为了保证这些模块之间能够稳定统一,那就要求这个公共部分是唯一的,各个模块之间所获取的数据与状态才能够保持一致
这时候我们使用到了单例模式,顾名思义,就是单个实例。
优点:
(1)提供了对唯一实例的受控访问。
(2)由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑能够提高系统的性能。
(3)可以根据实际情况的需要,在单例模式的基础上扩展为双例模式和多例模式。
缺点:
(1)单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单职责原则”。
(2)如果实例化的对象长时间不利用,系统会认为它是垃圾而进行回收,这将导致对象状态的丢失。
答:代理( proxy)模式,即为目标对象指定代理对象,并由代理对象代替目标对象控制客户端对目标对象的访问。
答:单例模式就是保证一个类只存在一个实例,只初始化一次,第一次完成初始化以后,在重复使用的时候,返回的都是这个实例,而不是新建一个实例。如果实例化的对象里面的属性值已经改变,就不能用单例了,只能通过原型模式重新实例化,原型模式允许多次创建实例对象。
答:
组合模式是表示对象的“部分-整体”层次结构的一种设计模式;组合模式将对象组合成树状结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
使用组合模式的情况:
(1)当想表示对象的“部分-整体”层次结构(树状结构)时可以使用组合模式
(2)在希望用户忽略组合对象与单个对象的不同并且统一地使用组合结构中的所有对象时使用组合模式。
答:
function BusinessOne(name){ this.name = name; //订阅者的集合 this.subscribers = new Array(); } //订阅者的发送消息的方法(推模式) BusinessOne.prototype.delive = function(news){ var self = this; //给每一个订阅者发送消息 this.subscribers.forEach( function(fn){ //调用接受者处理信息的函数 fn(news,self); } ) } //扩展公共订阅的函数,和取消订阅的函数 Function.prototype.subscribe = function(publisher){ var that = this; //some 访问数组度i型并且以参数的形式传回回调函数中 //只要至少有一次返回是true那么some就是true var alreadyExists = publisher.subscribers.some( function(el){ //处理不能重复订阅的功能 if(el == that){ return; } } ); //没用订阅你就可以订阅 if(!alreadyExists){ publisher.subscribers.push(that); } return this; } //取消 Function.prototype.unsubscribe = function(publisher){ var that = this; publisher.subscribers = publisher.subscribers.filter( function(el){ if(el !== that){ return el; } } ); return this; };
答:Gulp 是基于node.js的一个前端自动化构建工具,开发这可以使用它构建自动化工作流程,即前端集成开发环境。 使用gulp你可以简化工作量,让你把重点放在功能的开发上,从而提高你的开发效率和工作质量。 例如:你可以用gulp可以网页自动刷新,和MVVM开发模式很相似,如果你对vue.js有所了解的话,那么你一定不会陌生。你也可以使用gulp对sass进行预处理、代码检测、图片优化压缩、只需要一个简单的指令就能全部完成。
答:
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
总结:
从构建思路来说
gulp和grunt需要开发者将整个前端构建过程拆分成多个Task
,并合理控制所有Task
的调用关系
webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
对于知识背景来说
gulp更像后端开发者的思路,需要对于整个流程了如指掌 webpack更倾向于前端开发者的思路
答:应用gulp的gulp-uglify,gulp-minify-css模块完成