变量类型
- 值类型和引用类型
- 值类型:number 、string 、boolean 、undefined 、null
- 引用类型:除了值类型之外的值,例如:object、array 、function
- 值类型只能存储一个值
- 值类型复制就是值类型本身
- 引用类型复制只是复制引用类型的指针,并不是真正的值的靠包,他们之间是会相互干预的
- 引用类型特点:可以无限制的扩展属性
- JS中使用typeof能得到哪些类型:7种类型
变量计算-强制类型转换
-
字符串拼接(加减运算)
-
== 、 === 运算符(何时用 === 、==)
-
if语句(几个值转false)
-
逻辑运算 && ||
JS有哪些内置函数
如何理解JSON
- json和string互相转换
原型和原型链
- 构造函数
function Foo(name,age){
this.name = name;
this.age = age;
this.class = 'class-Foo'
// return this; 默认返回
}
var f = new Foo('dog',3)
- 构造函数-扩展
var a = {} // 其实是 var a = new Object()的语法糖
var b = [] // 其实是 var b = new Array()的语法糖
function Foo(){} // 其实是var Foo = new Function(){}的语法糖
- 原型规则和示例
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了‘null’)
- 所有的引用类型,都具有proto属性(‘proto’),属性值是一个普通的对象
- 所有的的函数,都有一个prototype属性,属性值也是一个普通对象
- 所有的引用类型,proto属性值指向它的构造函数的prototype属性
- 当试图得到一个对象的某个属性时,如果对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找
示例:
var arr = []
var obj = {}
arr.prototype.name = 'arrName' // TypeError: arr.prototype is undefined
arr.__proto__.name = 'arrName'
Array.prototype.age = 'arrAge'
arr.__proto__ === Array.prototype // true
function Fun(){}
Fun.prototype.funName = 'Fun' // 相当于添加属性 prototype: Object { funName: "Fun"}
Fun.__proto__.funSex = 'man'
Fun.__proto__ === Function.prototype // true
- 循环对象自身的属性方法
-
for … in 循环遍历对象自身和继承的可枚举属性(不含Symbol属性)
-
Object.keys(obj)
- Object.keys返回一个数组,包括对象自身的(不含继承的)所以可枚举属性(不含Symbol属性)的键名。
- 大多数时候,我们只关心对象自身的属性。所以,尽量不要用for…in 循环,而用Object.keys()代替
- Object.values(obj):方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
- bject.entries():方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
-
- 原型链
所有引用类型都具有proto属性,如果对象本身没有某个属性,则通过proto向上查找,最终都没有这个属性则返回null ,这种通过proto一级一级向上查找,则形成了原型链。
var obj={};obj.a=100;
var arr=[];arr.a=100;
function fn(){}. // var fn = function(){}
fn.a=100;
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
console.log(fn.prototype) // {a:100}
console.log(obj.__proto__===Object.prototype). // true
function Foo(name,age){
this.name=name;
}
Foo.prototype.alertName=function(){
alert(this.name)
}
var f=new Foo('zhangsan');
f.printName=function(){
console.log(this.name);
}
f.printName(); // zhangsan
f.alertName(); // alert zhangsan
//循环对象自身的属性
var item;
for(item in f){
//高级浏览器已经在for in中屏蔽了来自原型的属性
//但是这里建议大家还是加上这个判断,保证程序的健壮性
if(f.hasOwnProperty(item))
console.log(item);
}
*原型链*
f.toString()//要去f.__proto__.__proto__中去找
*instanseof*
**注意:** //用于判断 引用类型 属于哪个构造函数的方法
f instanceof Foo的判断逻辑是:
f的__proto__一层一层向上找,能否对应到Foo.prototype
f instanceof Object也是正确的
面试题1. 如何准确判断一个变量时数组类型?
var arr=[]
arr instanceof Array //true
typeof arr//object,typeof是无法判断是否是数组的
面试题2. 写一个原型链继承的例子
①function Animal(){
this.eat=function(){
console.log('animal eat');
}
}
function Dog(){
this.bark=function(){
console.log('dog bark')
}
}
Dog.prototype=new Animal();
var hashiqi=new Dog()
②function Elem(id){
this.elem=document.getElementById(id);
}
Elem.prototype.html=function(val){
var elem=this.elem;
if(val){
elem.innerHTML=val;
return this;//链式操作
}else{
return elem.innerHTML;
}
}
Elem.prototype.on=function(type,fn){
var elem=this.elem;
elem.addEventListener(type,fn);
return this;
}
var elem=new Elem("div1");
elem.html("<p>hello world</p>").on("click",function(){alert("clicked")}).html("<p>javascript</p>");
面试题3、描述new一个对象的过程
①创建一个新对象
②this指向这个新对象
③执行代码,即对this赋值
④返回this
面试题4、zepto(或其他框架)源码中如何使用原型链
①阅读源码是高效提高技能的方式
②但不能“埋头苦钻”有技巧在其中
③慕课网搜索“zepto设计和源码分析”
执行上下文
js是解释型语言不是编译型语言,所以有些错误在编译时不会出现,什么时候执行什么时候出现。
- 范围:一段<script>或者一个函数之内都会生成一个上下文
- 全局:变量定义,函数声明 / 执行之前,一段<script>会生成全局上下文
- 函数:变量定义,函数声明,this,arguments //函数执行之前会生成函数上下文
- 注意:‘函数声明’和‘函数表达式’的区别
console.log(a);//undefined
var a=100
fn('zhangsan') //'zhangsan' 20
function fn(name){
age=20;
console.log(name,age);
var age;
}
this
this要在执行时才能确认值,定义时无法确认
var a={
name:"A",
fn:function(name){ // 这里的this是a
console.log(this.name) // A
return function(){
console.log(this.name) // 闭包中的this是指向window
}
}
}
a.fn(); // A this===a
a.fn('huahua'); // A
a.fn('huahua')(); // console的值与 window.name值相同
a.fn.call({name:B}) // B this==={name:'B'}
var fn1=a.fn;
fn1() //this===window
使用场景
①作为构造函数执行
function Foo(name){
//this={};
this.name=name;
//return this
}
var f=new Foo('zhangsan')
②作为对象属性执行
var obj={
name:'A',
printName:function(){
console.log(this.name)
}
}
obj.printName();
③作为普通函数执行
function fn(){
console.log(this) //this===window
}
fn()
④call apply bind
function fn1(name,age){
alert(name);
console.log(this) //this===window
}
fn1.call({x:100},'zhangsan',20) // alert('zhangsan')
fn1.apply({x:100},['zhangsan',20])
var fn2=function(name,age){
alert(name);
console.log(this) //this==={x:100}
}.bind({x:100})//bind只能用函数表达式,函数声明不可用,会报错
fn2('zhangsan',200)
作用域
①没有块级作用域
if(true){
var name='zhangsan'
}
console.log(name)//'zhangsan'
②只有全局和函数作用域
var a=100;
function fn(){
var a=200;
console.log('fn',a)
}
console.log('global',a)
fn()
作用域链
var a=100
function fn(){
var b=200
//当前作用域没有定义的变量,即'自由变量'
console.log(a)
console.log(b)
}
fn()
var a=100;
function F1(){
var b=200;
function F2(){
var c=300;
console.log(a);//a是自由变量
console.log(b);//b是自由变量
console.log(c);
}
F2()
}
F1();
** 注意** :函数的父级作用域是函数定义时候的作用域,不是函数执行的作用域,哪个作用域定义了这个函数,这个函数父级作用域就是谁,跟函数执行没有关系。函数自由变量要到父级作用域中找,就形成了作用域链。
闭包
参考我的另一篇博客
问题1、说一下对变量提升的理解
①变量的定义
②函数的声明(注意和函数表达式的区别)
问题2、说明this几种不同的使用场景
参考上文**this**
问题3、创建10个`<a>`标签,点击的时候弹出来对应的序号
var i;
for(i=0;i<10;i++){
(function(i){
var a=document.createElement('a');
a.innerHTML=i+'<br>';
a.addEventListener('click',function(e){
e.preventDefault();
alert(i);
})
document.body.appendChild(a);
})(i);//相当于创建了10个函数
}
问题4、如何理解作用域
①自由变量
②作用域链,即自由变量的查找
③闭包的两个场景
问题5、实际开发中闭包的应用
//闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad(){
var _list=[];
return function(id){
if(_list.indexOf(id)>=0){
return false;
}else{
_list.push(id);
return true;
}
}
}
//使用
var firstLoad=isFirstLoad();
firstLoad(10);
firstLoad(10);
firstLoad(20);
//你在 isFirstLoad 函数外面,根本不可能修改掉_list的值
异步、同步
参考另一篇博客