js笔记-01(es5)

305 阅读7分钟

JS篇

变量

  • 声明变量的方式varletconst
  • 多个变量尽量用同一个var声明
var a = 10,
    b = 20,
    c = 30;
  • 变量命名规则:以字母、下划线、符号开头;可以包含数字、字母、下划线、符号开头;可以包含数字、字母、下划线、;不可以使用系统的关键字和保留字作为变量的命名

数据类型

  • 原始值:numberstringbooleanundefinednullSymbol
  • undefined:变量经过声明但是没有赋值
  • null:占位
  • 引用值数据类型:ObjectArrayfunctionDateregExp
var arr = [1, 2];
var arr1 = arr;
arr.push(3); // 地址不变
console.log(arr); // [1, 2, 3]
console.log(arr1); // [1, 2, 3]

arr = [3, 4]; // 重新开辟一块空间
console.log(arr); [3, 4]
console.log(arr1); [1, 2, 3]

操作符

+

  • 算术运算、字符串链接
  • 任何数据加上一个字符串都等于一个字符串
  • ()优先级最高,=优先级最低

++--

a = a + 1;
a++;
a += 1; // 推荐

var a = 1;
b = a++ + 1; // 先运算再执行a = a + 1
console.log(b); // 2
console.log(a); // 2

b = ++a + 1; // 先执行a = a + 1再运算
console.log(a, b); // 3 4

%/*

console.log(1 % 0); // NaN
console.log(1 / 0); Infinity

><

  • 字符串比较的是asc码;
console.log('A' > 'a'); // false; A -- 65,a -- 97
console.log('10' > '8'); // false

&& || !

  • undefined、null、"", 'false', NaN, 0转换为boolean值都是false
  • &&(找假值就结束):前面表达式的值转换boolean值为false直接返回前面的值,否则返回后面表达式的值,依次下去直到遇到假或者执行完所有表达式
var a = 3 && 5; // a = 5
var b = 0 && 5; // b = 0
  • ||(找到真值就结束):前面表达式的值转换成boolean值为true,直接返回前面表达式的值,否则直接返回后面表达式的值
var a = 1 || 2; // a = 1
var b = 0 || 4; // b = 4
var c = 0 || 'false'; // c = 'false'
undefined && -1 + NaN + ""; // undefined && NaN + "" => undefined && 'NaN' => undefined
typeof(a); // a未定义,打印undefined不报错

引用类型

数组

var arr1 = new Array(10);
console.log(arr1); // [undefinedx10]
console.log(arr1.length); // 10

var arr2 = new Array(10, 2, 3);
console.log(arr2.lenght); // 3
console.log(arr2); // [10, 2, 3]

打印斐波那契数列

var num = parseInt(prompt(''));
var firstNum = 1,
    secondNum = 1,
    thirdNum;
if(num > 2){
    for(var i = 1; i < num - 2; i++){
        thirdNum = firstNum + secondNum;
        firstNum = secondNum;
        secondNum = thirdNum;
    }
    console.log(thirdNum);
}else{
    console.log(1);
}

数字反序输出

var num = parseInt(prompt(''));
var reverseNum = Number(num.split('').reverse().join(''));

输出100以内的质数

var count = 0;
for(var i = 1; i < 100; i++){
    for(var j = 1; j <= Math.sqrt(i); j++){
        if(i % j === 0){
            count++;
        }
    }
    if(count === 1){
        console.log(i);
    }
    count = 0;
}

switch() case

switch(n){
    case 1:
        console.log(1);
        break;
    case 2:
        console.log(2);
        break;
}

typeof

  • 输出的都是字符串类型
  • 可以得到numberstringfunctionobjectbooleanundefined
var num = 123;
console.log(typeof(typeof(num))); // string

Number、parseInt、parseFloat、toFixed

  • Number
Number(null); // 0
Number(undefined); // NaN
Number('a'); // NaN
Number(true); // 1
Number(false); // 0
Number('123'); // 123
  • parseInt(a, b); a: 参数,b:进制
parseInt(8, 5); //NaN,因为5进制里面没有8
parseInt(10, 5); // 5,这里的10不是'十'是进制里面的10,10对应5进制就是5,10对应2进制就是2

parseInt(123.4); // 123
parseInt(true); // NaN
parseInt(false); // NaN
parseInt('123.4'); // 123
parseInt('123adfs'); // 123
  • parseFloat
parseFloat('123.12'); // 123.12
parseFloat('123.12sdfsdf'); // 123.12
  • toFixed:保留几位有效数字
var num = 123.123;
num.toFixed(1); // 123.1

toString

  • toString(a); a:进制
var a = 3;
a.toString(2); // '11'
a.toString(); // '3'

charAt()

const str = 'adasf';
console.log(str.charAt(0)); // a
console.log(str[0]); // a

获取字符串字节长度

  • str.charCodeAt(i):返回字符串i位置的字符的 Unicode 编码
function getStrLength(str){
    var count = 0;
    var len = str.lenght;
    for(var i = 0; i < len; i++){
        if(str.charCodeAt(i) <= 255){
            count += 1;
        }
        if(str.charCodeAt(i) > 255){
            count += 2;
        }
    }
    reutrn count;
}

function getStrLenght(str){
    var len = str.length;
    var count = len;
    for(var i = 0; i < len; i++){
        if(str.charCodeAt(i) > 255){
            count ++;
        }
    }
    return count;
}

隐式转换

isNaN(12); // false
isNaN('asd'); // true, 'asd'调用Number转换为NaN再判断isNaN
undefined == null; // true
NaN == NaN; // false

函数

函数声明

function func(a, b, c){
    arguments.length; // 2, 实参长度
    func.length; // 3, 形参长度

    // 映射关系
    a = 3;
    console.log(arguments[0]); //3
    
    arguments[1] = 4;
    console.log(b); 4
    
    c = 5;
    console.log(arguments[2]); //undefined,由于c未传入实参,所以这里是undefined
}

func(1, 2)

函数表达式

// 函数表达式定义之后函数会变成匿名函数
// 命名函数表达式
var func = function test(){

}
console.log(func);  // var func = function test(){}
console.log(test); //报错: test is not define
console.log(func.name) // test

// 匿名函数表达式
var func = function (){

}
console.log(func.name); // func

预编译

  • 函数声明整体提升;变量声明提升
  • 任何变量未经声明直接使用,此变量就为全局window所有
function test(){
    var a = b = 123; // b = 123=> var a=>a = b
}
windown.a; // undefined
windown.b; // 123
test()
  • 一切声明的全局变量都是window的属性
var a = 123;
windown.a; // 123

预编译过程按照以下过程

  1. 创建AO对象
  2. 找形参和变量声明,将变量声明和形参作为AO的属性名
  3. 将形参和实参统一
  4. 在函数体里找函数声明,值赋予函数体
1function test(a){
    console.log(a); // function a(){}
    var a = 123;    
    console.log(a); // 123   
    function a(){}
    console.log(a)  // 123
    var b = function(){}
    console.log(b)  // function(){}
    function d(){}
}
test(1)
// 预编译过程
GO: {
    test:undefined => function test(){}
}
AO: {
    a: undefined => 1 => function a(){},
    b: undefined => ,
    d: undefined => function d(){},
}

2global = 100;
function fn(){
    console.log(global);  // undefined
    global = 200;
    console.log(global);  // 200
    var global = 300;
}
// 预编译过程
GO: {
    global: 100,
    fn: function fn(){}
}
AO: {
    global: undefined
}

3var x = 1,y = z = 0;
function add(n){
    return n = n + 1;
}
y = add(x);
function add(n){
    return n = n + 2;
}
z = add(x);
console.log(x, y, z);
 // 预编译
 GO:{                        
     x: undefined,
     y: undefined,
     z: undefined,
     add: undefined, => function add(n){return n = n + 1} => function add(n){return n = n + 3}
 }
// 此时add() => function add(n){return n = n + 3}
// 所以最终:x = 1, y = 4, z = 4 

作用域、作用域链

闭包

  • 当内部函数被保存到外部时,将会生成闭包
  • 闭包会导致原有作用域链不释放,造成内存泄漏
1function test(){
    var num = 100;
    return function b(){
        num ++;
        console.log(num);
    }
}
var func = test();
func(); // 101
func(); // 102

2function test(){
    var num = 100;
    function add(){
        num ++;
        console.log(num);
    }
    function reduce(){
        num --;
        console.log(num);
    }
    
    return [add, reduce];
}

var func = test();
func[0](); // 101
func[1](); // 100
  • 闭包应用一(防抖)
function debounce(callback, dely){
    var timer = null;
    return function(){
        clearInterval(timer);
        var argus = arguments;
        var _this = this;
        timer = setTimeout(function(){
            callback.apply(_this, argus);
        },dely || 500)
    }
}
  • 闭包应用二(节流)
function throttle(callback, dely){
    var timer = null;
    return function(){
        if(timer){
            return false;
        }
        var argus = arguments;
        var _this = this;
        timer = setTimeout(function(){
            callback.apply(_this, argus)
            timer = null;
        },dely || 500)
    }
}

立即执行函数

  • 正确写法
(function(){}()); // w3c 推荐写法
(function(){})();
  • 错误写法
function(param){...}(1)
// 解析成如下,不报错但也不执行
function(param){...};
(1);
  • 打印序号
// 错误写法
function print(){
    var arr = [];
    for(var i = 0; i < 10; i++){
        arr[i] = function(){
            console.log(i)
        }
    }
    return arr;
}

var printNum = print();
for(var j = 0;j < 10; j ++){
    printNum[j](); // 10 10 10 10 10 10 10 10 10 10
}

// 解决办法,可以通过let声明替换掉var声明,这里介绍立即执行函数解决法
function print(){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        (function(j){
            arr[j]=function(){
                console.log(j);
            }
        }(i))
    }
    return arr;
}
var printNum = print();
for(var j = 0;j < 10; j ++){
    printNum[j](); // 0 1 2 3 4 5 6 7 8 9
}

// 输出dom元素序号
function test(){
    var liCollection = document.getElementByTagName('li');
    var lenght = liCollection.length;
    for(var i = 0; i < lenght; i ++){
        (function(j){
            liCollection[j].onclick = function(){
                console.log(j)
            }
        }(i))
    }
    return liCollection;
}

,操作符

  • 先看前面表达式再看后面表达式,再把后面表达式计算结果返回
1var a = (1 - 1, 2 + 1); // a = 3

2var f = (
    function a(){
        return '1';
    },
    function b(){
        return 1;
    }
)();
typeof(f); // number

3var a = 1;
if(function f(){}){
    x += typeof f;
}
console.log(x); // 1undefined

对象、包装类

对象

  • 对象创建方法
1、字面量
var a = {};

2、构造函数
var b = new Object();

3、自定义
function Car(name){
    this.width = 400;
    this.height = 200;
    this.name = name;
    
    // return this; 隐式返回一个this
    // return 123; 如果返回一个原始值,则默认返回this
    // return {}; 如果返回一个空对象,则通过构造函数构造出来的对象都是空{}
}

var car1 = new Car('宝马');
var car2 = new Car('大众');

console.log(car1.name, car2.name); // 宝马 大众

4、构造函数创建的对象相互独立
function Person(){
    var a = 0;
    function func(){
        a ++;
        console.log(a);
    }
    this.add = func;
}

var person1 = new Person();
person1.add(); // a = 1;
person1.add(); // a = 2;
var person2 = new Person();
person2.add(); // a = 1

包装类

1、length 的截断作用
var arr = [1, 2, 3, 4];
arr.length = 2;
console.log(arr); // [1. 2]

2、对于原始数据调用一个不存在的属性
var str = 'abc';
console.log(str.lenght); // 发生过程:先new String('abc').length再自动删除
console.log(str); // abc
var test = typeof(str); // test = string
if(test.length === 6){
    test.assign = '这是一个属性'; // new String('string').assign = '这是一个属性'“再自动删除”
}

/**
*这里是new String('string').assign,再自动删除
*注意这里的new String('string').assign 与上面的new String('string').assign是不同的
*每次调用完都会自动删除
*/
console.log(test.assign); // undefined

原型、原型链

原型

  • 原型是function对象的一个属性,它定义了构造函数构造出的对象的公有祖先。通过该构造函数构造的对象可以继承该原型的属性和方法。原型也是对象
  • 利用原型的特点和概念,可以提取公有属性
  • 对象如何查看原型:隐式属性__proto__
  • 对象如何查看构造函数:constructor
  1. 看看下面这个基本例子
Person.prototype.lastName = 'html';
Person.prototype.say = function(){
    console.log('say')
}
function Person(){
    this.lastName = 'css';
}
var person1 = new Person();
var person2 = new Person();
person1.say(); // say
  1. 我们来尝试修改原型属性
var obj = {
    name: 'html',
}
Person.prototype.name = 'css';
function Person(){
    // 当new Person()的时候,内部会做如下操作。
    // var this = {
    //    __proto__ : Person.prototype
    // }
}
var person = new Person();
person.name; // css
person.__proto__ = obj;
person.name; // html
  1. 我们先来看看下面这个对象操作,再看看45的区别,a.name = 'xxx'是操作a里面的ame属性,如果是a = {}那就是给a重新开辟一块空间,注意这两者区别。
var a = {
    name: 'aaa',
}
var b = a;
console.log(b.name); // aaa
a.name = 'bbb';
console.log(b.name); // bbb
a = {
    name: 'ccc',
}
console.log(b.name); // bbb
console.log(a.name); // ccc
  1. 改变prototype上属性的值
Person.prototype.name = 'aaa';
function Person(){};
var person = new Person();
console.log(person.name); // aaa
Person.prototype.name = 'bbb';
console.log(person.name); // bbb
  1. prototype重新赋值,由于__proto__的地址已经固定了
Person.prototype.name = 'aaa';
function Person(){};
var person = new Person();
console.log(person.name); // aaa
Person.prototype = {
    name: 'bbb',
}
console.log(person.name); // aaa
  1. 换个位置,为什么56会出现差异呢,这是因为__proto__new Person();的时候才生成,生成person的时候Person.prototype已经改变;再看看7就明白了

Person.prototype.name = 'aaa';
function Person(){};
Person.prototype = {
    name: 'bbb',
}
var person = new Person();
console.log(person.name); // bbb
  1. 先生成person1,改了prototype再生成person2
Person.prototype.name = 'aaa';
function Person(){};
var person1 = new Person();
console.log(person1.name); // aaa
Person.prototype = {
    name: 'bbb',
}
var person2 = new Person();
console.log(person1.name); // aaa
console.log(person2.name); // bbb
  1. 指定原型
    • 绝大多数对象的最终都会继承自Object.prototype
    • Object.create(null);没有原型
    • 判断变量类型Object.prototype.toString().call(123)--->'[Object Number]'
var obj = {
    name: 'aaa',
    age: 18
}
var person = Object.create(obj);
console.log(person.name); // aaa
  1. 重写原型方法
var num = 1;
num.toString(); // '1'
Number.prototype.toString = function(){
    return 100;
}
num.toString(); // 100
  1. 自身没有到原型链上找
var bar = {a:'002'};
function print(){
    bar.a = 'a';
    Object.prototype.b = 'b';
    return function inner(){
        console.log(bar.a); // a
        console.log(bar.b); // b bar上没有到原型链上找
    }
}
print()();

继承

  1. 共享原型
Son.prototype = Father.prototype;

function inherit(Target, Origin){
    Target.prototype = Origin.prototype;
}
  1. 圣杯模式
function Son(){}
function Father(){}
function inhert(Target, Origin){
    function F(){};
    F.prototype = Origin.prototype;
    Target.prototype = new F();
    Target.prototype.constructor = Target;
    Target.prototype.uber = Origin.prototype;
}
inherit(Son, Father);
const son = new Son();
const father = new Father();
  1. 私有化变量
function Person(name, age){
    var preAge = 25;
    this.name = name;
    this.age = age;
    this.say = function(){
        console.log(preAge);
    }
    this.changeAge = function(){
        this.age = preAge;
    }
}
var person = new Person('cc', 18);

命名空间

  • 管理变量,防止污染全局,适用于模块化开发
  • obj.name--->隐式转换为obj['name']

对象枚举

  • for in 能拿到原型上的属性,但是不会拿到顶端Object.prototype上的东西
  • in操作符能拿到原型上的属性,包括Object; 'name' in Object; // true
  1. hasOwnProperty判断属性是否是自身的
var obj = {
    name: 'aaa',
    age: 18,
}
for(var key in obj){
    if(obj.hasOwnProperty(key)){
        console.log(key); // key age
        console.log(obj[key]); // aaa 18
    }
}

判断数据类型

  1. instanceof
var obj = {};
var arr = [];
obj instanceof Object; // true
arr instanceof Object; // true
arr instanceof Array; // true
  1. constructor
obj.constructor; // Object
arr.constructor; // Array
  1. toString
Object.prototype.toString.call(obj); // [Object Object]
Object.prototype.toString.call(arr); // [Object Array]
Object.prototype.toString.call(123); // [Object Number]

this

  • 函数预编译过程this指向window
  • 全局作用域里面this指向window
  • call和apply可以改变this指向
  • obj.func(),func()中this指向obj
1var foo = 123;
function test(){
    var foo = 456;
    this.foo = 789;
    console.log(foo);
}
test(); // 456
console.log(foo); // 789

2var foo = 123;
function test(){
    this.foo = 456;
    console.log(foo);
}
// test(); // 456 ----this--->window
var obj = new test(); // 123 ---- this指向obj,所以this.foo = 456是obj={foo:456}
obj.foo; // 456

call/apply改变this指向

  • 相同点:都用来改变this指向
  • 不同点:传参方式不同,call接收参数列表,apply接收一个数组
  1. call
function Person(){
    this.name = 'aaa';
    this.age = 18;
}
var person = new Person();
console.log(person); // {name: 'aaa', age: 18}
var obj = {};
Person.call(obj, 'bbb', 20);
console.log(obj); // {name: 'bbb', age: 20}
  1. apply
function Person(name, age){
    this.name = name;
    this.age = age;
}
function Student(name, age, mobile){
    this.mobile = mobile;
    Person.apply(this, [name, age])
}
var student = new Student('student', 18, '13333333333');
console.log(student); // {name: 'student', age: 18, mobile: '13333333333'}

arguments

function test(){
    console.log(arguments.callee); // function test(){}
    console.log(arguments.callee === test); // true
}
test()
function test(){
    demo()
}
function demo(){
    console.log(demo.caller);// function test(){demo()}
}
test()

拓展

  1. 深克隆
function deepClone(origin){
    var target;
    if(typeof(origin)==='object'){
        if(origin instanceof Array){
            target = [];
            origin.forEach(e=>target.push(deepClone(e)));
            return target;
        }else if(origin instanceof Object){
            target = {};
            for(var key in origin){
                target[key] = deepClone(origin[key]);
            }
            return target;
        }
        
    }
    return origin;
}

以下数组方法改变原数组

  1. push()
Array.prototype.mypush = function(){
    var argus = [...arguments];
    argus.forEach(e=>this[this.length] = e);
}
  1. pop()删除数组最后一位
Array.prototype.mypop = function(){
    var len = this.length;
    var lastValue = this[len - 1];
    this.length = len - 1;
    reutrn lastValue;
}
  1. shift()删除数组第一位
  2. unshift()向数组前面添加数据
  3. reverse()
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]
  1. splice(从第几位开始(数字为负从后往前),截取多少长度,添加新的数据)
const arr = [1, 2, 3];
const a = arr.splice(0, 2);
console.log(a); // [1, 2]
console.log(arr); // [3]
  1. sort
  • 返回大于0的数,后面的数放前面
  • 返回值为负数,前面的数放在前面
  • 为0不动
  • 符合冒泡排序算法
  • a=1, b=2,3,1
const arr = [1, 2, 3, 1];
arr.sort(function(a,b){
    return a-b;
})

乱序
const arr = [1, 2, 3, 1];
arr.sort(()=>{
    return Math.random() - 0.5;
})

以下方法不改变原数组

  1. concat
const arr1 = [1];
const arr2 = [2];
const arr3 = arr1.concat(arr2); // [1, 2]
console.log(arr1); // [1]
console.log(arr2); // [2]
  1. toString()
const arr = [1, 2, 3];
arr.toString();
console.log(arr); // '1,2,3'
  1. slice(a,b) [a,b)左闭右开
const arr = [1, 2, 3, 4];
console.log(arr.slice(0, 2)); // [1, 2]
slice(a, b)  [a,b),左闭右开

Array.prototype.myslice=function(a,b){
    const len = this.length;
    const arr = [];
    for(let i = a; i < b;i+=1){
        arr.push(this[i]);
    }
    return arr;
}
  1. join
const arr= [1 , 2 ,3];
const arr1 = arr.join('-');
console.log(arr1); // 1-2-3
  1. split
const str = '1-2-3';
const arr = str.split('-');
console.log(arr); // [1, 2, 3]

数组去重

const arr = [1, 1, 1, 2, 2, 2, 'a', 'a'];
Array.prototype.unique = function(oldArr){
    const newArr = [];
    const obj = {};
    this.forEach(e=>{
        if(obj[e] === undefined){
            obj[e] = 123;
            newArr.push(e);
        }
    })
    return newArr;
}