本文共 9096 字,大约阅读时间需要 30 分钟。
一.闭包
最开始理解闭包是在一个函数内部定义一个函数,可以在外面的环境里进行调用。现在对于闭包的理解是利用函数来保存作用域内的对象。
理解闭包首先要理解执行上下文,变量对象,活动对象,作用域链。因为执行上下文在函数执行后会销毁,因此变量也同时消失,但是为了一些特殊的应用场景,因此需要在函数执行后依旧可以访问到函数内的变量。js语言将函数作为“一等公民”,可以作为参数进行传递,同时每个函数也拥有其作用域链,如果内部函数作为变量返回,那么它将带着它的作用域链一同返回。因为内部函数的作用域链中包含着外部函数的变量,函数声明和参数等,因此,外部函数即使被执行完了,变量也不会销毁,因为他们依旧被内部函数的作用域链引用。 举个例子:
1 2 3 4 5 6 7 8 | var scope = 'global scope' ; function foo() { var scope = 'local scope' ; return function bar() { console.log(scope); } } foo()(); |
执行结果是‘local scope’。因为在返回的bar函数被调用的时候,会先从自己的作用域链查找,没有的话会逐级再向上查找,直到找到scope对象,输出结果结束。
二.this
如果把上面的代码增加一句话,在返回的函数中输出this。
1 2 3 4 5 6 7 8 9 | var scope = 'global scope' ; function foo() { var scope = 'local scope' ; return function bar() { console.log(scope); console.log( this ); } } foo()(); |
结果是’local scope’,’window’。this指向的是global对象,在浏览器中就是window。 对this的理解是:
1)它是在解析函数的时候确定的,即执行上下文的一个属性,运行过程中并不会改变。
1 2 3 4 5 6 | function foo() { function bar () { ... } this = bar; //会报错 } |
2)this根据调用函数的对象或者表达式形式确定。
1 2 3 4 | function foo() { console.log( this ); } foo(); |
调用函数的对象是Global,即window
1 2 3 4 5 6 7 8 9 10 11 12 | var foo = { x:10, bar: function () { console.log( this .x); } } var zoo = { x:20 } zoo.bar = foo.bar; zoo.bar(); foo.bar(); |
输出20,10,调用函数的对象分别是zoo和foo
1 2 3 4 5 6 7 8 9 10 | x = 'global x' ; var foo = { x: 'local x' , bar: function () { console.log( this .x); } } foo.bar(); (foo.bar)(); (foo.bar = foo.bar)(); |
()表达式并没有改变函数本身的调用值,因此返回‘local x’,赋值表达式改变了函数本身的调用值,因此返回‘global x’。
3)new操作符调用的函数this指向构造函数对象
当函数作为构造器被调用的时候:
1 2 3 4 5 6 7 | function A() { alert( this ); // newly created object, below - "a" object this .x = 10; } var a = new A(); alert(a.x); // 10 |
在这种情况下,new操作符会生成一个新的对象,即构造函数定义的对象。 在对象创建之后,然后所有“A”函数中this的值会设置为新创建的对象。
可以通过函数对this值进行人为干预:
4)call和apply函数
1 2 3 4 5 6 7 8 9 10 | x = 'global x' ; var foo = { x: 'local x' , bar: function (n) { console.log( this .x); console.log(n); } } foo.bar.call({x: 'call x' }, 'call' ); foo.bar.apply({x: 'apply x' }, [ 'apply' ]); |
通过call和apply的第一个参数改变this的值。
参考文章:
对于js中原型的理解,我仅处在初级阶段。我的理解是,js的原型是为了实现“类”的概念,可以使得对象的方法可以通用,实现类的继承。虽然js中并没有类的概念,但是多数情况下,人们还是偏向于使用面向对象的概念在编程。
Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。当我们用js来模拟类时就涉及到了原型链的概念。原型链和作用域链的作用很相似,都是用来定义查询变量的一种规则。当你在自身查找不到某个变量或者方法的时候,原型链会根据规则继续向上查找,直到顶端为止。
比如,在定义一个“类”的时候,通常会先定义一个构造函数,里面包含了实例属性。
function Person (name, age) { this.name = name; this.age = age;}
他可能包含了一些通用的方法(类方法),比如读取姓名和年龄,安排班级等
Person.prototype.getName = function () { return this.name;}Person.prototype.getAge = function () { return this.age;}
当我们需要给一个类定义通用的方法的时候,我们需要在它的原型上定义属性,这样,所有通过构造函数生成的实例都可以调用该方法。
继承的实现也是因为有原型的存在得以实现,通用的实现继承方法如下:
function Person (name, age) { this.name = name; this.age = age;}Person.prototype.getName = function () { return this.name;}Person.prototype.getAge = function () { return this.age;}Person.prototype.setAge = function (age) { this.age = age;}function Teacher(name,age,no){ //实现子类属性的继承,每个实例都有属于自己的属性,同时需要注意参数的顺序,call函数只看重顺序,不在乎参数名称 Person.call(this, name, age); this.classNo = no;}Teacher.prototype = new Person(); //将父类的对象赋值给子类,原型对象,这样子类就继承了父类的方法
// 在支持ES5的浏览器推荐下面的写法:(创建一个新的对象,内容是Person.prototype,将其赋值给Teacher.prototype,这样就将Person的原型和Teacher的原型相关联,并不会产生副作用)// Teacher.prototype = Object.new(Person.prototype);
Teacher.prototype.getInfo = function () { console.log('name:' + this.name + 'age:' + this.age + 'classNo:' + this.classNo);}var teacherLi = new Teacher('Li', 30, 2);teacherLi.getInfo(); //name:Liage:30classNo:2teacherLi.setAge(28);teacherLi.getInfo(); //name:Liage:28classNo:2
一. new操作符的含义
当我们使用new操作符时,实际上就是创建一个对象。但在实际运行当中,new是创建了两个对象,并将其相互关联。例如上面的例子中var teacherLi = newTeacher('Li', 30, 2); 这句话,new操作符创建了一个对象并将它赋值给teacherLi,并将teacherLi和Teacher.prototype相联系,teacherLi.__proto__ === Teacher.prototype。
在ES5规范里,针对new的操作的定义如下:
简单理解就是:
1. 新建一个对象
2. 将对象的内部__proto__属性和构造函数的prototype相关联
3. 利用构造函数给实例对象属性赋值
4. 如果构造函数没有显示返回对象,则返回步骤一创建的对象
二.constructor
Foo.prototype.constructor === Foo
Foo.prototype的.constructor属性只是Foo函数在声明时的默认属性。如果prototype被重新赋值声明,那么constructor就不知道是指向谁了,它会根据原型链一直检索,直到检索到最上层Object对象。
function Foo() { /* .. */ }Foo.prototype = { /* .. */ }; // 创建一个新原型对象var a1 = new Foo();a1.constructor === Foo; // false!a1.constructor === Object; // true!
三.prototype&__proto__
对象的__proto__属性指向它关联的prototype对象。可以简单的理解为实例的__proto__属性指向它的原型对象。
function Foo(a) { this.a = a;}var foo = new Foo('foo');console.log(foo.__proto__ === Foo.prototype);console.log(Foo.__proto__ === Function.prototype);console.log(Function.__proto__ === Function.prototype);console.log(Function.prototype.__proto__ === Object.prototype);console.log(Object.prototype.__proto__ === null);
以上的答案都是true,可以看到当你用new标识符来创建一个属性时它会默认应用建立原型链,一直关联到Object这个对象原型。
在js的内置类型当中:
Number.__proto__ === Function.prototype // trueBoolean.__proto__ === Function.prototype // trueString.__proto__ === Function.prototype // trueObject.__proto__ === Function.prototype // trueFunction.__proto__ === Function.prototype // trueArray.__proto__ === Function.prototype // trueRegExp.__proto__ === Function.prototype // trueError.__proto__ === Function.prototype // trueDate.__proto__ === Function.prototype // trueMath.__proto__ === Object.prototype // trueJSON.__proto__ === Object.prototype // true
因为Function.prototype.__proto__ === Object.prototype得值是true,所以,函数也是对象。
之前看到过一套测试题,放在这里以供思考:
function Foo() { getName = function () { alert (1); }; return this;}Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};var getName = function () { alert (4);};function getName() { alert (5);} //请写出以下输出结果:Foo.getName();getName();Foo().getName();getName();new Foo.getName();new Foo().getName();new new Foo().getName();
解释地址:
前两天在网上看到了一道面试题,问iframe高度自适应的问题。发现自己之前几乎没有关注过iframe的问题,所以在这里记录一下。
原题目是: 页面A的域名是:http://www.taobao.com,页面B的域名是http://www.tmall.com,如果A使用iframe引用页面B,如何做到iframe的高度自适应(即B内容有多高,iframe就有多高)
在这里首先分析一下如果不涉及跨域或者只是主域相同,子域不同的情况下的解决方案:
父页面代码:
1 2 3 4 5Document 6 7 8 9 26 27
看到张鑫旭的博客里说到另一种方法,是在iframe页面传递一个参数给父页面,告知其高度。父页面取到参数后再给iframe高度赋值。
大致原理在子页面iframe里定义
// 为了防止window.location.hash产生跨域问题,可以直接写死hostUrl地址:利用window.top.location = 父页面地址(写死) + 锚点 var hostUrl = window.location.hash.slice(1);hostUrl += "#height=" + 1294;window.top.location = hostUrl;
然后将子页面嵌入到父页面中,父页面提取location中的height数值,从而更改iframe高度。
var iframeHeight = function() { var hash = window.location.hash.slice(1); if (hash && /height=/.test(hash)) { iframe.height = hash.replace("height=", ""); } setTimeout(iframeHeight, 200);};iframeHeight();
可以参考:
这里思考了一下是不是可以不写死页面的地址:
假设面试题目中提到的页面A:www.taobao.com内部嵌入页面B:www.tmall.com页面,要让B页面高度自适应的解决方案
参考各种资料,可以利用中间代理页面agent.html来完成。
主要原理是agent页面和A页面同源,将agent页面嵌入到B页面获取到B页面宽高后,通过url传递宽高值。通过agent来操作A页面中嵌入的B页面宽高。
1. 在A(taobao)页面嵌入iframe
2. 在B(tmall)页面嵌入agent_iframe,获取B页面的宽高。(将获取到的宽高通过url传递)
3. 在agent.html插入代码(因为agent和A页面是相同域名,所以可以通过子元素来控制父元素的父元素[因为agent是嵌入在B页面的,B页面嵌入在A页面,因此agent可以控制A页面的元素,此处为多层嵌套,有点绕]的宽高)
总结
个人认为,如果父页面的地址是固定的,我觉得直接写死地址是比较方便直观的方法。当然还有很多其他方法可以实现高度自适应。
详见:
看到的笔试题,总结在这里吧!
1.运用JS设置cookie、读取cookie、删除cookie
function setCookie (name, value) { let duringDay = 30; let exp = new Date(); // setTime() 方法以毫秒设置 Date 对象。 exp.setTime(exp.getTime() + duringDay*24*60*60*1000); // 防止Cookie中不允许需要保存的字符串中有“;”出现。有些操作系统,在解释中文的字符串时候常常会出现乱码的现象。避免储存数据中出现非英文字母、非数字的字符。运用escape编码 document.cookie = name + '=' + escape(value) + ';expires=' + exp.toGTMString(); } // setCookie('ga', 'aaaaa'); function getCookie (searchName) { let rsObj = {}; let rsArray = document.cookie.split(';'); rsArray.map((cv,index,array)=>{ let item = cv.split('='); //去掉空格 let name = unescape(item[0].split(' ').join('')); let value = unescape(item[1]); rsObj[name] = value; }); /* 或者利用正则 let arr; let reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); if(arr=document.cookie.match(reg)) { return unescape(arr[2]); } else { return null; } */ return rsObj[searchName]; } // getCookie('ga'); function deleteCookie(delName) { let exp = new Date(); exp.setTime(exp.getTime() - 1); let val = getCookie(delName); if (val) { // toGMTString() 方法可根据格林威治时间 (GMT) 把 Date 对象转换为字符串,并返回结果。 // Thu, 29 Dec 2016 10:48:00 GMT document.cookie = delName + '=' + val + ';expires=' + exp.toGTMString(); } } // deleteCookie('ga');
2. 请编写一个JavaScript函数 parseQueryString,它的用途是把URL参数解析为一个对象,如:var url = “http://witmax.cn/index.php?key0=0&key1=1&key2=2″;
function parseQueryString () { let query = window.location.search.substring(1); let arr = query.split('&'); let obj = {}; arr.map((cv,index,array)=>{ let item = cv.split('='); let name = decodeURIComponent(item[0]); let value = decodeURIComponent(item[1]); obj[name] = value; }); return obj; } // let foo = parseQueryString(); // console.log(foo);