1. 跨域问题
- jsonp: 利用script标签不受同源策略限制的特点, 协商好一个callback参数, 当然这个参数是由请求方定的
- h5新方法: window.postMessage, 首先要有一个src是另一个域页面的ifrmae标签, 并且监听一个onload事件, 然后在onload函数里获取到这个ifrmae标签, 并通过.contentWindow获取到另一个域的页面的window对象, 使用这个window对象调用postMessage('XXX')方法, 传递数据, 最后在另一个域的页面里, 调用window.onmessage事件, 通过 事件对象e.data获取传递的内容
- 还可以用服务器代理的方式, 我用过node和axios实现过
- window.name: 它有一个特性就是, 在窗口生命周期没有结束之前(未关闭之前)都只会共享一个window.name, 这样, 当我在a.html里设置了一个window.name, 页面跳转b.html后, 这个window.name不会改变, 还是a.html里设置的, 就相当于把a.html里的数据传到了b.html中. 具体实现:在data.html页面设置好window.name利用ifrmae标签载入data.html页面, 当ifrmae载入date.html时候, 用一个函数用来获取data.html的window.name,
2. 闭包
- js分全局作用域和函数作用域, 通过作用域链, 函数作用域可以访问到包含环境和全局作用域, 全局作用域无法直接访问到函数作用域, 只有通过闭包(函数中的函数), 将父级里的数据return出去, 然后在把闭包return出去, 闭包能一直访问到父级的数据, 说明父级里的数据一直存在在内存中, 所以闭包可能会导致内存泄露
- document.domain: 将两个页面的document.domain设置成一样的, 就可以通过ifrmae.contentWindow方法, 访问到通过ifrmae载入的页面的window对象了,也能传值了
3. css3动画
4. 页面性能问题解决和分析
- 浏览器渲染的过程
-- html转化为dom
-- css转化成cssom
-- 结合dom和cssom生成渲染树
-- 生成布局 flow
-- 将布局绘制到页面上 paint
*注意: 用js获取offsetXXX, scrollTop等还有getComputedStyle()的时候会立即出发回流(reflow), 所以不要吧这些读操作和写操作放到一个语句里
*原则: 样式表越简单, 回流和重绘越快. dom层级越高, 成本就越高. table成本高与div - 提高性能技巧:
-- 读操作写在一起, 写操作也在一起, 两种操作不要混在一起
-- 样式是通过回流(重排)得到的, 最好缓存下
-- 不要一条条改变样式, 尽量操作class
-- 如果需要对摸个元素进行多次操作, 可以先将其设置成display:none, 在随便操作n次, 完事后再display: block
-- 使用虚拟dom脚本, 例如react或vue
-- window.requestAnimationFrame()调节重绘 - js优化:
-- 减少作用域链查找, 在当前执行环境缓存一下包含环境或全局执行环境的数据;
-- 字符串拼接尽量避免使用 + , 使用数组的join();
-- 优化循环, 简化循环体, 简化终止条件, 比如 for(var i = 0,len= arr.length; i < len; i ++);
-- switch语句更快;
-- 变量声明提倡 一个var
-- 使用事件委托js用addlistenerEvent, jq用 on - 减少http请求, 合并css,js, 使用雪碧图,
- 图片懒加载
- 静态资源用cdn
- 按需加载资源requirejs
5. 用户交互设计理论
6. js事件绑定
7. 遇到比较难的问题, 怎么解决的
8. 移动端开发经验
9. canvas
10. js作用域 - 函数作用域链
11.原型继承:
// Student的构造函数
function Student(props) {
this.name = props.name || 'nonamed'
}
Student.prototype.sayHello = function() {
alert('Hello ' + this.name)
}
// 通过Student扩展出PrimaryStudent
function PrimaryStudent() {
Student.call(this, props)
this.grade = props.grade || 1
}
// 声明空对象
function F() {}
// 把空对象原型指向Student
F.prototype = Student.prototype
// 把PrimaryStudent的prototype指向F的实例化对象
PrimaryStudent.prototype = new F()
// 修复PrimaryStudent上的构造函数(constructor)
PrimaryStudent.prototype.constructor = PrimaryStudent
// 验证
var xiaoming = new PrimaryStudent({name: 'zxk', grade: 10})
xiaoming.name // 'zxk'
xiaoming.grade // 10
// 验证原型:
xiaoming.__proto__=== PrimaryStudent.prototype // true
xiaoming.__proto__ .__proto__=== Student.prototype // true
// 验证继承:
xiaoming instanceof PrimaryStudent // true
xiaoming instanceof Student // true
12 排序算法
// 冒泡排序
function sort(arr) {
for(var i = 0; i < arr.length; i ++) {
for(var j = 0; j < arr.length; j ++) {
if (arr[j] > arr[j + 1]) {
var oldJ = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = oldJ
}
}
}
}
//
13 将[1, 2, [3, [4, 5, [6]]]] => [1, 2, 3, 4, 5, 6]
function transArr(arr, res) {
var res = res || []
for(var i = 0; i < arr.length; i ++) {
if(arr[i] instanceof Array) {
transArr(arr[i], res)
} else {
res.push(arr[i])
}
}
return res
}
14 事件委托
- 好处, 减少dom操作, 提高性能, 为动态插入的元素绑定事件
- 原理事件冒泡, js -> addlistenerEvent, jQ -> on
- 使用on去绑定事件, 通过e.target获取实际点击的元素进行相应的操作
- jQ使用on去绑定事件, 第二个参数填选择器, 会将父级事件代理到这个选择器上(使用与动态插入的dom)
15 flex的兼容
- 旧版: display: box; 过渡: display: flex box; 新版: display: flex
- 安卓: 2.3+ display: -webkit-box; 4.4+ display: flex
- ios: 6.1+ display: -webkit-box; 7.1+ display: flex
- pc: ie10支持 flex, -ms形式
- 兼容写法, 都是向下兼容的, so旧版写法要放到下边, 否则就会不起作用
.box{
display: -webkit-flex; /* 新版本语法: Chrome 21+ */
display: flex; /* 新版本语法: Opera 12.1, Firefox 22+ */
display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
display: -moz-box; /* 老版本语法: Firefox (buggy) */
display: -ms-flexbox; /* 混合版本语法: IE 10 */
}
.flex1 {
-webkit-flex: 1; /* Chrome */
-ms-flex: 1 /* IE 10 */
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
-webkit-box-flex: 1 /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-flex: 1; /* OLD - Firefox 19- */
}
16 模块化requirejs
- 引入require.js
- 配置main.js
require.config({
baseUrl: 'js',
paths: {
app: 'app' // 如果是目录的话, 这里的名字要和目录一模一样, 如果是文件可以不同
}
})
- 在html里引入requirejs和data-main入口
//index.html
<script src="js/requirejs" data-main="main"></script>
- define一个模块
// a.js
define(function(){
return {
name: 'a'
}
})
// b.js
define(function(){
return {
name: 'b'
}
})
- 引入模块
// index.html
<script>
require(['app/a'], function(a) {
console.log(a)
})
setTimeout(function() {
require(['app/b'], function(b) {
console.log(b)
})
}, 3000)
</script>
-
目录结构
屏幕快照 2018-05-26 下午12.57.10.png
17 css布局
- 左边固定宽度, 右边自适应
- 左边: width: 100px; float: left;右边: width: auto;margin-left: 100px;
- flex: 左边固定宽度, 右边: flex: 1,
- 三栏布局左右固定宽度, 中间自适应
- 结构是: 左 - 右 - 中, 左: 固定宽度, float: left; 右: 固定宽度float: right; 中: width: auto; margin-left: 左宽, margin-right: 右宽
- css选择器:
- 类选择器.class
- id选择器: #id
- 标签选择器: p
- 后代选择器: div p
- 子代选择器: div>p
- 之后选择器: div+p
- 属性选择器:
- 伪类选择器:
- nth-child()
- first-child
- :before - after
18 for循环和定时器 与 闭包
//瞬间打出0 - 4
for(var i = 0; i < 5; i++) {
console.log(i)
}
for(var i = 0; i < 5; i++) {
setTimeout((function(){ // 自调用函数, 立即执行
console.log(i)
})(), i * 1000)
}
// 顺间打出0, 然后每隔一秒打出1-4
for(var i = 0; i < 5; i++) {
(function(i){
setTimeout(function() {
console.log(i)
}, i*1000)
})(i)
}
// 顺间打出5, 每隔1秒打出一个5, 共4个
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, i*1000)
}
for(var i = 0; i < 5; i++) {
(function(){
setTimeout(function() {
console.log(i)
}, i * 1000)
})(i)
}
// promise相关
setTimeout(function() {
console.log(1)
}, 0)
new Promise(function executor(resolve){
console.log(2)
for(var i = 0; i < 1000; i++) {
i == 999 && resolve();
}
console.log(3)
}).then(function() {
console.log(4)
})
console.log(5)
// logs 2 > 3 > 5 > 4 > 1, promise的then()会放在当前tick的最末尾, 但是setTimeout会把函数踢到下一轮tick中
18 js高阶面试
- 描述js中的继承和原型链, 并举例
- js并没有类的概念, 而是通过原型链实现的继承, 每个对象都会在内部引用一个prototype的对象, prototype也会引用自己的原型对象, 以此类推, 链条的末尾是null为原型的对象, 当一个对象引用了不属于自己的属性时,将会遍历原型链, 直到找到属性,或者找到链尾 null
- js中的对象和哈希表
- 对象本质就是一个hash表, 即键值对的集合, 键总是字符串, 由于js是弱类型语言, 当你传入的键并非字符串时候, 也不会报错, 而是隐式的使用toString()方法,
- 题目:
var foo = new Object() var bar = new Object() var map = new Object() map[foo] = 'foo' map[bar] = 'bar' console.log(map[foo]) // logs: 'bar' 解释: 由于foo和bar都不是,字符串, js会隐式的将foo和bar使用toString()方法,由于它们都是空对象, 所以它们转化的结果一样: [Object Object], 也就是说map[bar]覆盖了map[foo], map[foo]的结果也是覆盖后的结果
- 解释js中的闭包, 什么是闭包, 它们有什么特性, 如何使用, 举个例子
- 闭包是一个函数, 包含在创建闭包时处于作用域内的所有变量或其他函数. 闭包通过'内部函数'的形式实现的, 也就是另一个函数的主体内定义函数
- 闭包的一个特性: 闭包可以一直访问其包含函数的变量, 也就是说他们会一直存在在内存中不被释放
- 闭包可以防止变量被内存释放, for循环和闭包, 创建命名空间防止变量污染
-
描述创建对象的不同方式, 和各自影响, 提供示例
91527395499_.pic.jpg - 函数表达式(var foo = function (){})和函数语句(function foo(){})定义函数的区别?
- 函数语句可以在定义之前被调用(通过hoisting技术, 总是使用函数的最后一个定义),
function foo(){ return 1} console.log(foo()) // 结果为: 2 function foo() {return 2}
- 函数表达式, 则不能在函数定义之前被调用
- 将 js源文件里的内容封装到一个函数里, 这样做的重要性和原因?
- 相当于为它创建了一个闭包, 创建一个私有的命名空间,避免命名冲突
- 为全局变量提供一个容易引用的别名
(function($){ ... })(jQuery)
-
==
和===
有什么区别, 举个例子-
==
比较前会有隐式转换, 而===
是严格的类型比较 -
123 == '123'
是成立的结果为true,123 === '123'
是不成立的, 结果为false
-
- js文件开头的
use strict
是什意思?- js的严格模式, 对js代码执行严格的解析和错误处理, 没有直接调用者的函数, this指向'undefined'
19 js中的this指向(非严格模式下)
函数中的this在函数被定义的时候并没有确定下来, 只有函数被调用的时候,才知道它到底指向谁
- 原则1: 一个函数没有被上级调用, 那么this指向window
function foo() { console.log(this) // window } foo()
- 原则2: 一个函数被上级调用, 那么this指向上级函数
var o = { a: 10, c: function() { console.log(this.a) // 10 console.log(this) // o } } o.c()
- 原则3: 一个函数中包含多个对象, 尽管这个函数是被最外层的对象调用, 但是this也只会指向它的上级
var o = { a: 10, b: { a: 12, fn: function() { console.log(this.a) // 12 console.log(this) // b } } } o.b.fn()
- 原则4:
var o = { a: 10, b: { a: 12, fn: function(){ console.log(this.a) // undefined console.log(this) // window } } } var j = o.b.fn j()
- 原则5: 构造函数中的this: new 可以改变对象的this指向 -> new后边的函数
function Fn() { this.user = 'zxk' } var a = new Fn() console.log(a.user) // 'zxk'
- 原则6: this和return
- 构造函数里return {}, this指向, 这个对象
function Fn() { this.name = 'zxk' return { name: 'ret - zxk' } } var xx = new Fn() console.log(xx.name) // 'ret - zxk'
- 构造函数里return function(){}, this指向, 这个函数,但是没有继承这个函数的prototype, 打印出来的是空
function Fn() { this.name = 'zxk' return function() {} } var xx = new Fn console.log(xx.name) // 啥也没有
- 构造函数里return 非对象, this指向原来的这个构造函数
function Fn() { this.name = 'zxk' return 1 } var xx = new Fn() console.log(xx.name) // 'zxk'
- call, apply和bind
- 改变函数的this执行, 并没有继承原型链
- call, apply会立即执行, bind需要()调用
- 常用的字符串和数组方法:
- slice(start, end): string直接返回重start开始到end(不包括start)的部分,可以为负
- splice(index, howmany, item1, item2....,itemn): 从数组的index开始, 删howmany个元素, 替换成item1 - itemn
- concat() 合并两个数组
- substring(start, stop): 返回字符串start到stop(不包含)之间的字符, 不接受负值, 如果stop为负值, 那么就取start之前的值
- substr(start, [length]): 返回字符串从start开始的, 共计length个, 可以接受负值
20 js经典面试题
// code 1
var length = 10;
function fn() {
alert(this.length);
}
var obj = {
length: 5,
method: function() {
fn();
}
};
obj.method(); // 最后相当于fn(), 函数没有被任何上级调用, this指向window
// code 2
var num = 100;
var obj = {
num: 200,
inner: {
num: 300,
print: function() {
console.log(this.num);
}
}
};
obj.inner.print(); //300, this指向inner
var func = obj.inner.print;
func(); //100 默认绑定,this指向window
obj.inner.print(); //300 thi指向inner
(obj.inner.print = obj.inner.print)(); //100 this指向window
// code 3
function foo() {
console.log(this.a);
}
var obj2 = { a: 42, foo: foo };
var obj1 = { a: 2, obj2: obj2 };
obj1.obj2.foo(); // 42 this指向他的直接上级obj2
var obj3 = { a: 2 };
foo.call(obj3); // 2, this指向call的参数obj3
var bar = function() {
foo.call(obj3);
};
bar(); // 2, 就相当于foo.call(obj3)
setTimeout(bar, 100); // 2, bar的this指向window, 但是执行bar的时候, 其实执行的是foo.cal(obj3),
bar.call(window); // 2, 同上
var obj4 = { a: 3, foo: foo };
obj2.foo(); // 42, this指向obj2
obj4.foo(); // 3, this指向obj4
obj2.foo.call(obj4); // 3 this指向call的参数obj4
obj4.foo.call(obj2); // 42 this指向call参数obj2
// code 4
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a是全局对象的属性
setTimeout(obj.foo, 100); // "oops, global", setTimeout里的this都会指向window, 相当于var fun = obj.foo, fun()
obj.foo(); // 2, this指向obj
// code 5 (new绑定)
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2 new可以改变this, 让bar的this指向foo
var obj1 = { foo: foo };
var obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2, foo(2)的this指向obj1, this.a相当于给obj1添加a属性
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3, obj1的this指向obj2并传参数, 相当于给obj2, 添加a属性
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4
// code 6
function foo() {
console.log(this.a);
}
var a = 2;
// 如果你把null或者undefined作为this的绑定对象传入call\apply\bind,这些值在调用的时候会被忽略,实际应用的是默认绑定规则。
foo.call(null); // 2
var bar = foo.bind(null);
bar(); // 2
foo.apply(undefined); // 2
// code 7 箭头函数
function foo() {
return a => console.log(this.a);
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };
var bar = foo.call(obj1);
bar.call(obj2); // 2 箭头函数是根据外层(函数或者全局)作用域来决定this。并且绑定后无法修改。
-
- 字符串倒序
function reverseString(str) { var strArray = str.split(""); // 使用空字符串来分割成字符数组 return strArray.reverse().join(""); // 反转并连接 } reverseString("hello");
-
- 下面代码执行结果是啥, 如何实现每隔1秒输出1 - 5
for (var i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); }
每个1秒秒输出6共输出5个, 循环终止条件是i <= 5, i最终值是6的时候, 循环才结束, setTimeout会把函数踢出本次任务队列, 等循环结束后, 才打印console.log(i), i那个时候已经是6了,
- 套个闭包, 并把每次循环的i缓存起来,并传进闭包就ok
- 用let声明 变量: 每次循环都会重新声明一个i
for(let i = 0; i < 5; i++) { setTimeout(function() { console.log(i) }, i*1000) }
-
- 代码执行结果
3 + "3" // '33' "23" > "3" // false var b = true && 2; // undefined "abc123".slice(2, -1) // 'c12' "abc123".substring(2, -1) // 'ab' //如果 start or stop 是负数或 NaN,会把它当成 0 对待;如果 start > stop,则会交换这两个参数
var foo = 1,
bar = 2,
j,
test;
test = function(j) {
j = 5;
var bar = 5;
console.log(bar); // 5
foo = 5;
};
test(10);
console.log(foo); // 5 改变的全局变量
console.log(bar); // 2 由于函数作用域对全局作用域的隐藏,所以只有在test函数内部,bar=5,并不能影响到全局中的bar
console.log(j); // undefined test(10)函数调用的时候,是函数内部的参数j接收到了10,但是它也是函数作用域内的变量,并不会改变全局作用域中的j。
if (!("sina" in window)) {
var sina = 1;
}
console.log("sina:", sina); // undefined
// 变量提升: js会把所有的通过var声明的全局变量, 提升到最顶层, 所有sina液在if之前就存在了, 也不会走if()分支
// 相当于
var sina
f (!("sina" in window)) {
sina = 1;
}
console.log("sina:", sina); // undefined
function SINA() {
return 1;
}
var SINA;
console.log(typeof SINA); // function
// 重复声明被忽略了, 所以var没有生效
- 数组去重, 带obj的元素
var arr = [2, [1,2], 3, "2", "a", "b", "a", [1, 2]]
function quchong(arr) {
var map = {}
var res = []
arr.forEach(item => {
if(!map[JSON.stringify(item)]) {
res.push(item)
map[JSON.stringify(item)] = 1
}
})
return res
}
console.log(quchong(arr))
function foo() {
"use strict";
console.log(this.a);
}
function bar() {
console.log(this.a);
}
var a = "this is a 'a'";
bar(); // "this is a 'a'"
foo(); // "TypeError: Cannot read property 'a' of undefined
alert(a); // 输出函数体
a(); // 10
var a = 3;
function a() {
alert(10);
}
alert(a); // 3
a = 6;
a(); // 报错a不是一个function
alert(a); // undefined
a(); // a is not a function
var a = 3;
var a = function() {
// 函数表达式
alert(10);
};
alert(a); // 输出函数体
a = 6;
a(); // a is not a function 因为a已经赋值成6了
- 查看字符串中最多的元素
function findMax(str) {
var map = {} // 存储str每个元素和对应数量的对象
var max = {num: 0} // 存储最大值的对象
for(var i in str) {
if(map[str[i]]) {
map[str[i]] ++
} else {
map[str[i]] = 1
}
if(map[str[i]] > max.num) {
max.num = map[str[i]]
max.key = str[i]
}
}
return max
}
var max = findMax(_str)
console.log(max)
21 数组方法和字符串方法
- 数组方法:
- pop(): 删除数组最后删一个元素, 并返回这个元素
- push(): 在数组末尾添加一个元素, 并返回数组的length
网友评论