JS基础知识系统整理(不断更新)

一、基础知识

1.变量类型

  • JS中使用typeof 能得到哪些类型
  • 何时使用 === 何时使用 ==
  • JS中有哪些内置函数
  • JS变量按照存储方式区分为哪些类型,并描述其特点
  • 如何理解JSON

值类型:

每个变量存储各自的值,不会相互影响,number , string , boolean 

var a = 100;
var b = a;
a = 200;
console.log(b);    //100

引用类型:

变量只是通过一个指针指向对象,改变了age对象,由于a也指向该对象,所以a指向的age对象为21, object , array , function

var a = {age:20}
var b = a
b.age = 21
console.log(a.age)    //21

typeof 运算符

有六种类型:undefined , string , number , boolean , object , function ; typeof只能区分值类型的详细类型;

typeof {}    //object
typeof []    //object
typeof null    //object
typeof console.log    //function

2.变量计算 - 强制类型转换

字符串拼接

var a = 100 + 10    //110
var b = 100 + '10'    //10010

==运算符

obj.a == null ——> obj.a === null || obj.a === undefined

(除了 obj.a == null 用== 其余都用=== ,jQuery 源码推荐写法)

100 == ‘100’    //true
0 == ''    //true
null == undefined    //true

if语句

var a = true
if(a){//...}

var b = 100
if(b){//...}

var c = ''
if(c){//...}

if中转换为false的情况:

if(0){}
if(NaN){}
if(''){}
if(null){}
if(false){}

 

逻辑运算符

console.log(10 && 0)    //0
console.log('' || 'abc')    //'abc'
console.log(!window.abc)    //true  因为window.abc 是undefined

判断一个变量会被当作true还是false

var a = 100;
console.log(!!a) 

JS中的内置函数

Object , Array , Boolean , Number , String , Function , Date , RegExp , Error

(注意:Math 是对象,不是函数)

理解JSON

//JSON 是 JS 一个内置对象而已

JSON.stringify({a:10, b:20})   对象转换字符串

JSON.parse('{"a":10, "b":20}')  字符串转换成对象

 

3.原型和原型链

  • 如何准确判断变量是数组类型
  • 写一个原型链继承的例子
  • 描述new 一个对象的过程
  • zepto源码中如何使用原型链

构造函数

function Foo(name, age){
    this.name = name;
    this.age = age;
    this.class = 'class-1'
    //return this  //默认有这一行
}

var f = new Foo('zhangsan',20)
// var f1 = new Foo('lisi',22)    //可创建多个对象

 

开头字母须大写

new Foo()时把参数传进去,this会先变成空对象,挨个赋值之后,会return出来给f,

f就具备了调用属性的能力。

构造函数扩展

  • var a = {} 其实是 var a = new Object() 的语法糖
  • var a = [] 其实是 var a = new Array()的语法糖
  • function Foo(){...} 其实是 var Foo = new Function(...)
  • 使用instanceof 判断一个函数是否是一个变量的构造函数(判断一个变量是否为“数组”: 变量 instanceof Array)

原型规则和示例

  • 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了“null” 以外)
  • //自由扩展属性
    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn (){}
    fn.a = 100;
  • 所有的引用类型(数组、对象、函数),都有一个 __proto__属性(隐式原型),属性值是一个普通对象
    console.log(obj.__proto__);    //Object
    console.log(arr.__proto__);    //Array[0]
    console.log(fn.__proto__);    //function(){}
  • 所有的函数,都有一个prototype属性(显式原型),属性值也是一个普通的对象
    console.log(fn.prototype);    //Object{}
  • 所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的"prototype"属性值,(指向和完全等是一个概念)
    console.log(obj.__proto__ === Object.prototype);    //true

     

    • 当试图得到一个引用类型值的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找。
      function Foo(name,age){
          this.name = name;
      }
      Foo.prototype.altername = function(){
          alert(this.name);
      }
      
      var f = new Foo('zhangsan');
      f.printname = function(){
          console.log(this.name);
      }
      
      f.printname();
      f.altername();
      f.toString();  //要去 f.__proto__.__proto__中查找

       

 循环对象自身的属性:

var item;
for(item in f){
    //高级浏览器已经在 for in 中屏蔽了来自原型的属性
    //但是这里建议大家还是加上这个判断,保证程序的健壮性
    if(f.hasOwnProperty(item)){
        console.log(item);
    }
}

 

原型链:

 

instanceof

判断引用类型 属于哪个构造函数的方法

比如 f instanceof Foo 的判断逻辑是:

f 的 __proto__ 一层一层往上,能否对应到Foo.prototype,再试着判断 f instanceof Object

 

原型链继承的例子

基本:

//动物
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);
}

var div1 = new Elem('div1');

div1.html('<p>hello world</p>').on('click', function(){
    alert('clicked');
})

描述new一个对象的过程

  1. 创建一个新对象
  2. this 指向这个新对象
  3. 执行代码,即对 this 赋值
  4. 返回 this

 

4、作用域和闭包

  • 说一下对变量提升的理解
  • 说明this几种不同的使用场景
  • 创建 10 个<a>标签,点击的时候弹出来对应的序号
  • 如何理解作用域
  • 实际开发中闭包的应用

执行上下文

在执行代码前,先把定义声明获取一遍,再由上到下执行

  • 范围:一段<script>或者一个函数
  • 全局:变量定义、函数声明
  • 函数:变量定义、函数声明、this、arguments

PS:注意“函数声明”和“函数表达式”的区别

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(){
        console.log(this.name);        
    }
}

a.fn(); //this === a
a.fn.call({name: 'B'}); //this === {name: 'B'} 
var fn1 = a.fn;
fn1()   //this === window

 

 

几种场景:

  • 作为构造函数执行
    • function Foo(name){
          this.name = name;  //this === Foo
      }
      var f = new Foo('zhangsan');
  • 作为对象属性执行
  • var obj = {
        name : 'A',
        printName : function(){
            console.log(this.name);
        }
    }
    obj.printName();  //this === obj

     

  • 最为普通函数执行
  • function fn(){
        console.log(this);
    }
    fn();  //this就是Window

     

  • call apply bind (其实就是用来改变this指向的)
  • function fn1(name,age){
        alert(name);
        console.log(this);
    }
    fn1.call({x:100}, 'zhangsan', 20);  //this就是{x:100}
    fn1.apply({x:100}, ['zhangsan', 20]);  //this就是{x:100}
    
    var fn2 = function(name,age){
        alert(name);
        console.log(this);
    }.bind({y:200});
    fn2('zhangsan',20);//this就是{y:200}

     

作用域

  • 没有块级作用域
  • if(true){
        var name = 'zhangsan';
    }
    console.log(name);

     

  • 只有函数和全局作用域
  • var a = 100;
    function fn(){
        var a = 200;
        console.log('fn',a);
    }
    console.log('global',a)
    fn();

 

作用域链

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();

 

一个自由变量,一直不断的去往它的父级作用域找,形成了一个链式结构

闭包

function F1(){
    var a = 100;

    //返回一个函数(函数作为返回值)
    return function(){
        console.log(a)
    }
}

//f1 得到一个函数
var f1 = F1();
var a = 200;
f1();   //100

 

使用场景:

  • 函数作为返回值(上一个例子)
  • 函数作为参数传递
  • function F1(){
        var a = 100;
        return function(){
            console.log(a)  //这个a首先去声明的作用域找
        }
    }
    
    var f1 = F1();
    function F2(fn){
        var a = 200;
        fn();
    }
    F2(f1); //100

     

实际开发的应用:

//闭包实际应用中主要用于封装变量,收敛权限
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) //true firstLoad(10) //false firstLoad(20) //true
//在isFirstLoad 函数外,根本不可能修改掉 _list 的值

 

创建 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(){
            alert(i);
            return false;
        })
        document.body.appendChild(a);
    })(i)
};

 

for (var i = 0; i < 10; i++) {
    (function(i){
        var a = document.createElement('a');
        a.innerHTML = '第'+i+'个<br>';
        a.onclick = function(){
            alert(i);
        }
        document.body.appendChild(a);
    })(i)
};

 

 5、异步和单线程

  • 同步和异步的区别是什么?分别举一个同步和异步的例子
  • 一个关于setTimeout的笔试题
  • 前端使用异步的场景有哪些

什么是异步

console.log(100)
setTimeout(function(){
    console.log(200)
},1000)
console.log(300)

//100
//300
//200

异步和同步最大的区别在于有没有阻塞程序的进行 

 

对比同步

console.log(100)
alert(200)
console.log(300)
//不点击确认,程序会一直卡,不输出300

 

何时需要异步

  • 可能发生等待的情况
  • 等待过程中不能像 alert 一样阻塞程序运行
  • 因此,所有的“等待的情况”都需要异步

使用异步场景:

  • 定时任务:setTimeout,setInterval
  • 网络请求:ajax请求,动态<img>加载
  • 事件绑定
//ajax请求代码实例  响应事件需要等待
console.log('start');
$.get('./data1.json',function(data1){
    console.log(data1);
})
console.log('end');
//<img>加载实例  图片加载需要等待
console.log('start');
var img = document.createElement('img');
img.onload = function(){
    console.log('loaded');
}
img.src = '/xxx.png';
console.log('end');
//事件绑定实例  触发事件需要等待
console.log('start');
document.getElementById('btn1').addEventListener('click',function(){
    alert('clicked');
})
console.log('end');

 

 

单线程

JS是单线程语言,所有的异步程序会被拿出去先不执行,主线程执行完后,它要看边上有没有等待的程序需要执行

console.log(100)
setTimeout(function(){
    console.log(200)
})
console.log(300)

//100
//300
//200

 

  • 执行第一行,打印100
  • 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事)
  • 执行最后一行,打印300
  • 待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行
  • 发现暂存起来的setTimeout 中的函数无需等待时间,就立即过来执行

一个关于setTimeout的笔试题

console.log(1);
setTimeout(function(){
    console.log(2);
},0)
console.log(3);
setTimeout(function(){
    console.log(4);
},1000)
console.log(5);
//1
//3
//5
//2
//4

 

六、其他知识

  • 获取 2018-01-23 格式的日期
  • 获取随机数,要求是长度一致的字符串格式
  • 写一个能遍历对象和数组的通用 forEach 函数

日期

Date.now()  //获取当前时间毫秒数
var dt new Date();
dt.getTime()    //获取毫秒数
dt.getFullYear()    //
dt.getMonth()   //月(0-11)
dt.getDate()    //日(1-31)
dt.getDay()     //星期(0-6)
dt.getHours()   //小时(0-23)
dt.getMinutes() //分钟(0-59)
dt.getSeconds() //秒(0-59)

 

 

Math

获取随机数 Math.random() (0-1之间的小数)

常见作用:清除缓存

 

数组API

  • forEach 遍历所有元素
  • var arr = ['苹果','香蕉','西瓜'];
    arr.forEach(function(item,index){
        //遍历数组的所有元素
        console.log(index,item);
        //0 "苹果"
        //1 "香蕉"
        //2 "西瓜"
    
    })

     

  • every 判断所有元素是否都符合条件
  • var arr = [1,2,3];
    var result = arr.every(function(item,index){
        //用来判断所有的数组元素,都满足一个条件
        if(item < 4){
            return true;
        }
    })
    console.log(result);    //true

     

  • some 判断是否有至少一个元素符合条件
  • var arr = [1,2,3];
    var result = arr.some(function(item,index){
        //用来判断所有的数组元素,只要有一个满足条件
        if(item < 2){
            return true;
        }
    })
    console.log(result);    //true

     

  • sort 排序
  • var arr = [1,4,2,3,5];
    var arr2 = arr.sort(function(a,b){
        //从小到大排序
        return a - b;
        //从大到小排序
        //return b - a
    })
    console.log(arr2);

     

  • map 对元素重新组装,生成新数组
  • var arr = [1,2,3,4];
    var arr2 = arr.map(function(item,index){
        //将元素重新组装,并返回
        return '<b>' + item + '</b>';
    })
    console.log(arr2);
    //["<b>1</b>", "<b>2</b>", "<b>3</b>", "<b>4</b>"]

     

  • filter 过滤符合条件的元素
  • var arr = [1,2,3,4,5];
    var arr2 = arr.filter(function(item,index){
        //通过某一个条件过滤数组
        if(item >= 2){
            return true;
        }
    });
    console.log(arr2);//[2, 3, 4, 5]

     

对象API

var obj = {x:100,y:200,z:300};
for(var key in obj){
    //如果key是自身的属性,那么打印出来
    if(obj.hasOwnProperty(key)){
        console.log(key,obj[key]);
    }
}

 

 

获取 2018-01-23 格式的日期:

var dt = new Date();

function formatDate(dt){
    if(!dt){
        dt = new Date();
    }
    var year = dt.getFullYear();
    var month = dt.getMonth() + 1;
    var date = dt.getDate();

    if(month < 10){
        month = '0'+month;
    }
    if(date < 10){
        date = '0'+month;
    }

    return year + '-' + month + '-' + date;
}
console.log(formatDate(dt));

 

 

获取随机数,要求是长度一致的字符串格式:

var random = Math.random();
random = random + '0000000000';//后面加上10个0
random = random.slice(0,10);
console.log(random);

 

 

写一个能遍历对象和数组的通用 forEach 函数

function forEach(obj,fn){
    var key;
    if(obj instanceof Array){
        //准确判断是不是数组
        obj.forEach(function(item,index){
            fn(index,item);
        });
    }else{
        //不是数组就是对象
        for(key in obj){
            fn(key,obj[key]);
        }
    }
}
//使用
var arr = [1,2,3];
//注意,这里参数顺序换了,为了和对象的遍历格式一致
forEach(arr,function(index,item){
    console.log(index,item);
})

var obj = {x:100,y:200};
forEach(obj,function(key,value){
    console.log(key,value);
})

 

posted @ 2018-04-20 22:27  言叶以上  阅读(440)  评论(0编辑  收藏  举报