js基础

201 阅读4分钟

[toc]

const、 let 、var、this关键字

const 修饰之后不再改变,必须初始化。const的作用域是块级作用域,并且const不存在变量提升。

全局变量是存储在栈区

由绑定点击事件引发的思考。

let只在作用域里有效,可在函数和代码块里重复声明,let是存在暂存死区。全局变量和函数的定义在预解析,也就是预编译的过程中,会进行变量hoist。这个过程被分为创建,初始化,赋值的过程。

    for (let i = 0; i < row.length; i++) {
        console.log("局部绑定" + i);//打印局部绑定1234,不出循环不能是5
        row[i].onmouseover = function() {
            row[i].className = "tr-focus";
        };
    }

这样写是可执行,但是变量改为var就不行了

    // 全局变量只是绑定,触发事件时才执行
    var i;
    for (i = 0; i < row.length; i++) {
        row[i].onmouseover = function() {
            this.className = "tr-focus";
            console.log("全局变量" + i);//打印1234,出循环变为5
        };
    }

事件只是绑定,未能触发,所以是绑定着执行完i=5,这个是很好理解,触发的函数事件调用的i指向的是栈中?是存在栈里面的吧,i是公开的,谁都可以操作。

而let呢?

绑定事件执行完后,不再对i进行更改,这个循环到头了,这个空间按理说是被要释放的。

除非每个被let创建绑定的i都有一个独属于它的空间,姑且叫他隐藏作用域。五个点击,五个i空间,由于没办法跳出循环,不知道是不是还有一个统筹计数的i值。如果有,也就是被开辟六块空间,0,1,2,3,4,5

所以i到底有多少空间?

var整个生命周期

let iterable = [10, 20, 30];
        const i = 10;
        TAG("i : " + i);
        for (const value of iterable) {
            // const定义的只读功能,value不能赋值。
            // value = value + 10;
            console.log(value);
        }
        for (let value of iterable) {
            value = value + 10;
            console.log(value);
        }

const不能赋值,而是他将变量指向某块地址,这块地址如果是对象,对象里的值是可以被改变的。

        const person = {
            name: "zoe",
            age: 12
        };
        person.name = "zhang";

但不能新建一个对象,再赋予给原地址。只可以改变地址内的指向。

/**
 * 代办事件类,包含事项是否做完,创建事项的时间,任务是什么
 */
function Matter() {
    let matter;
    let isDone = false;
    let time = new Date().getTime();
    console.log(time);

    //this
    this.getTime = function() {
        return time;
    }
    this.setMatter = function(str) {
        matter = str;
    }
    this.setThisMatter = function(str) {
        this.matter = str;
    }
    this.getMatter = function() {
        return matter;
    }

    this.Done = function() {
        isDone = true;
    }
}
var mat1 = new Matter();
mat1.setMatter("今天要做的是测试list是否可以融合各种对象");
mat1.setThisMatter("今天要做的是测试list是否可以融合各种对象3231231");

console.log(mat1.getMatter());//今天要做的是测试list是否可以融合各种对象
console.log(mat1.matter);//今天要做的是测试list是否可以融合各种对象3231231

var mat2 = new Matter();
mat2.setMatter("你要知道js是弱类型,也就是不需要像java给出泛型T");
var mat3 = new Matter();
mat3.setMatter("看看结果吧");

var list = new List();
list.append(mat1);
list.append(mat2);
list.append(mat3);
console.dir(list);
console.log(list.getElement(0).matter);//今天要做的是测试list是否可以融合各种对象3231231

由此可见,let创建的变量和this创建的变量不是在同一个区域内,用let可以实现私有化封装。

Switch判断

switch 中 case的判断是===的判断,即数据类型和值的双重判断,这点要注意。

另外switch的判断条件可以是String 、Number、Boolean、char、枚举、null、undefined

do while都会执行一遍

IE6下的for- in 、for 循环、for of

遍历数组类型,for- in只会打印分配了空间的元素,可以用来处理稀疏数组,for循环未被分配空间的将以undefined显示。

for-in和for的区别,遍历时,for-in出来的是字符串型数字,不能直接进行几何运算,需要强转。for-in只遍历枚举类型,for是直接取

for-in是无法遍历iterator的,for of却可以。

www.jianshu.com/p/897c7ca4c…

js中null与undefined的区别

undefined相当于给没有赋值的变量,函数,属性自动上的一个保险,不是针对对象,这样就不会抛出空指针的异常。null就是不指向任何地址,是针对对象,就被垃圾回收。

在java中,NULL之后是不能被调用的。知道确定类型去调用,这个undefined是无类型。

null与undefined 是不一样的,在js中,去控制界面元素的属性,如果给选中元素的属性上undefined的值,html元素显示的会是默认的属性,显示"默认值",如果是null值,h3会无显示。

选中img图片也是,null会报图片无法显示。undefined加载默认图片。

<h3 class="name">默认值</h3>

js的垃圾回收机制(GC-garbage collection)

这块和java相同

GC标记清除算法 引用计数(循环引用,即A引用一次,计数器加1,A与B嵌套引用,A引用B,B引用A,锁住了,不能被回收)

www.cnblogs.com/zhwl/p/4664…

正则表达式

/i不区分大小写

js的变量提升

this关键字的四种绑定

www.cnblogs.com/xiaohuochai…

默认绑定

在全局环境中,this

显示函数的绑定

闭包

闭包其实是函数,让函数A的一个内部的函数B开放出一个接口,去访问函数A内部的值,函数B将由一个变量接受,由变量的生命周期控制整个函数A内部的值的生命周期

延长了函数A中的局部变量的存活时间。

同时,只有通过变量和开放的接口访问函数A的值,达成私有

    var list = document.querySelectorAll("li");
    for (var i = 0; i < list.length; i++) {

        // 相当于创建了三个函数,单独分配了3个index变量的内存
        // 每个onclick函数指向index的值,事实上,增加了空间
        // let也一样,let存储的区域是在暂存,let和var产生的变量可以重名,由此不在一个区域
        (function(index) {
            list[index].onclick = function() {
                console.log(index);
                console.log("同时可以使用i:" + i);
            }
        })(i);
    }

高阶函数

函数作为参数,返回值返回,就是一个高阶函数

精确度

64位浮点数,2的53次方到-2的53次方

作用域链

私有的实现

因为JavaScript中没有私有公有的概念,所有的分为局部和全局,所以要私有的话,只能够变为let,用es6去实现。

 function People(name, age) {
            this.name = "dasads";
            this.age = age;

            //n 等同于私有,通过getN得到
            let n = 121;
            }
            this.getN = function getN() {
                return n;
            }

            this.setN = function setN(n) {
                this.n = n;
            }

继承的实现

原型链

继承的实现有多种方式,首先了解下原型链的概念。是因为js中创建对象的方式有多种,有对象直接量,通过new直接创建对象,原型。

原型链构造函数是其中一种重要方式,节省内存。

字面量创建对象
var book ={
    name:"尼采";
    read: function(){}
}

通过new创建对象
var book = new book(name);
原型

每一个js对象除了null都与一个对象相关联。所有通过对象直接量创建的对象都具有一个同一原型。

通过Object.prototype获得是对原型对象的引用,可直接添加原型属性,原型方法。

比如var date = new Date();

date拥有Date.prototype的所有属性,Date.prototype是继承Object.prototype,也就是date同时继承Date.prototype、Object.prototype。 形成了链条式,这就是原型链。

function ProtoClass(name) {
        this.name = name;
        this.printName = function() {
            TAG("构造函数声明的" + name);
        }
    }
    ProtoClass.prototype = {
        name: "prototype-zoe",
        // 直接写name是为什么获得不了
        printProtoName: function() {
            TAG("原型链中声明的打印" + name);
        }
    }
  • prototype添加属性和方法比在构造函数中消耗内存更小,因为一个类的原型在内存中只有一个,写在原型中的方法被所有的实例共享,实例化的时候并不会再在实例中复制一份。

  • 而写在类中的方法,实例化的时候会在每一个实例对象中在复制一份,所以消耗的内存更高。

  • 而在构造函数中定义的属性和方法要比在原型中定义的属性和方法优先级高

原型链继承
function Father(name, age, sex) {
        this.name = name || 'Father';
        this.age = age;
        this.sex = sex || false;

        let secret = "爸爸的秘密是不喜欢吃韭菜";
        this.eat = function eat() {
            TAG("我是人类我的在吃东西");
        };
        this.getSecret = function() {
            return secret;
        };

        this.setSecret = function(str) {
            secret = str;
        }
        this.coding = function() {
            return "爸爸的技能是会敲代码";
        }
    }
    function Son() {

    }
    //缺点:这一句实际上把Son定死了,Son只能拥有父亲的属性,不能实现多继承。
    Son.prototype = new Father("xiaomi", 12, "true");
    Son.prototype.hobby = "i can sing.that is my hobby.";
    Son.prototype.sing = function() {
        return "i will show you my songs.";
    }
    var xiaomi = new Son();

    xiaomi.name = "xiaomi";
    xiaomi.age = '12';
    TAG("儿子的名字" + xiaomi.name);
    TAG("儿子的性别" + xiaomi.sex);
    TAG("儿子是否直接访问爸爸的秘密:" + xiaomi.secret);
    TAG("儿子征求访问爸爸的秘密,同时也获得了父亲的秘密:" + xiaomi.getSecret());
    TAG("儿子开始成长,拥有其他属性。" + xiaomi.hobby + xiaomi.sing());

上诉代码父类能够封装私有,通过getter 和setter开放接口,提供子类获取,子类可以拥有其他属性,父类不能访问,缺点是不能实现多继承。

优点是直接在子类原型链创建属性,节省空间。

既是子类的实例,也是父类的实例

    TAG(xiaomi instanceof Son); //true
    TAG(xiaomi instanceof Father); //true
构造函数继承

父类不变,添加其他父类

    function Mother(name, age, sex) {
        this.name = name || 'Mother';
        this.age = age;
        this.sex = sex || true;
        this.painting = function() {
            return "会画画";
        }
    }
function Daughter(name, age, hobby) {
        Father.call(this);
        Mother.call(this);
        this.name = name;
        this.age = age;

        this.rap = function() {
            return "小红成长后,学会了rap.";
        }
    }
    var xiaohong = new Daughter("xiaohong", 9, "she can dance");

    TAG("小红是否继承了妈妈的技能会画画?" + xiaohong.painting());
    TAG("小红是否继承了爸爸的技能?" + xiaohong.coding());
    TAG("小红同时继承了父亲和母亲" + xiaohong.rap());

优点:利用Object.call()函数实现了多继承。

缺点:实例化的过程,是对子类的实例化,并非对父类的实例化。

    TAG(xiaohong instanceof Daughter); //true
    TAG(xiaohong instanceof Father); //false

只能继承父类的实例属性和方法,不能继承原型的方法。

无法实现函数的复用,事实上在内存中,每个子类都对父类进行拷贝,影响性能。

call函数

call(argsthis,args1,args2,args3s……)和apply()

call方法源码实现

        function Person(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.eat = function(name) {
                console.log(name + "is eatting");
            }
        }
        
        //创建的对象就是一个Function
       Function.prototype.esCall = function(context) {
        
            // 将当前对象Person的地址给context.fn,也就是传入对象重新创建了一个fn变量去指向当前函数
            context.fn = this;
            
            // 一个新的参数数组去获取传入的参数
            var args = [];

            for (var i = 1; i < arguments.length; i++) {
                args.push('arguments[' + i + ']');
            }
            
            // eval是指获取一个字符串,去执行字符串里的代码
            var result = eval('context.fn(' + args + ')');
            delete context.fn;
            return result;
        }
 		function Son(name, age) {
 		
            // 由于不是静态方法
            Person.esCall(this, name, age);

            this.eating = function(name) {
                return "到底是" + name + "在吃";
            }

        }
        var p = new Person("父亲");
        var s = new Son("儿子", 12);
        console.dir(s);

如此实现esCall去实现继承是可以的,但前提是传参顺序一定是context,args……这样的顺序,但实际上,call函数可以直接去使用。

比如

var arr = [1, 2, 3, 4, 6, 79];
var max = Math.max.call(1, 4, 57, 78, 6);
console.log(max);
//打印78

但此时调用我们写好的esCall会报错,因为是从i下标1开始做判断,也就是给定参数1会被认为是context。

但1不是,所以加判断,要判定当前获取的元素是不是一个Function,如果是,照之前的写,如果不是,那么参数则从下标0开始算,并且要把未传入的context值,指向这个函数本身

也就是Math.max.call指向的是它本身

以下是我非常丑陋的办法

        Function.prototype.esCall = function(context) {
            var args = [];

            //判断context是否是个函数,不是函数,直接执行当前函数的代码
            if (!(context instanceof Function)) {
                for (var i = 0; i < arguments.length; i++) {
                    args.push('arguments[' + i + ']');
                }
                var result = eval('this(' + args + ')');
                return result;

            } else {
                context.fn = this;
                for (var i = 1; i < arguments.length; i++) {
                    args.push('arguments[' + i + ']');
                }
                // eval是指获取一个字符串,去执行字符串里的代码
                var result = eval('context.fn(' + args + ')');
                delete context.fn;
                return result;
            }
        }

        var arr = [1, 2, 3, 4, 6, 79];
        var max = Math.max.call(1, 4, 57, 78, 6);

        console.log(max); //78

        max = Math.max.esCall(79, 4, 57, 78, 6);
        console.log(max); //79

var result = eval('this(' + args + ')');

context.fn = this;

var result = eval('context.fn(' + args + ')');好像很没必要,很重复,主要是在参数下标i的判断上,如果直接是context = this解决问题,那么context是指向下标0的数字,context一旦被指向当前函数后,那么下标0的参数就遗失了。

第一个参数不对,无法再执行正确的函数。所以得保留下下标0的参数,不想动参数的坐标,就不能动context,就只能动这句var result = eval('this(' + args + ')');

写出来的代码真丑陋

好了好了,我知道了(熊猫招手)。

但这里还有个问题,万一传入的对象本身

var arr1 = ['秋天', '夏天', '春天'];
arr1.push("冬天");
//不传入arr1会报错,为什么?
//如果不传入,this,将指向null
arr1.push.call(arr1, "调用call加入");
console.log(arr1);//长度为5
arr1.push.esCall(arr1, "调用esCall加入");
console.log(arr1);//长度为6
        Function.prototype.esCall = function(context) {
            var args = [];
            //判断context是否是个函数,不是函数,直接执行当前函数的代码
            //判断context是否是个对象,因为对象()是不能执行函数,改变函数的指向
            if (!(context instanceof Function || context instanceof Object)) {
                for (var i = 0; i < arguments.length; i++) {
                    args.push('arguments[' + i + ']');
                }
                var result = eval('this(' + args + ')');
                return result;

            } else {
                context.fn = this;
                for (var i = 1; i < arguments.length; i++) {
                    args.push('arguments[' + i + ']');
                }
                // eval是指获取一个字符串,去执行字符串里的代码
                var result = eval('context.fn(' + args + ')');
                delete context.fn;
                return result;
            }
        }

往后面学,可以学到这种程度

segmentfault.com/q/101000000…

github.com/jawil/blog/…

apply函数

apply函数和call函数类似,只有一个区别,apply 是传入一个多个参数的数组,call传入的是以逗号连接的多个参数

bind函数

bind又减少了一个,call函数return是执行代码的结果,而bind是return的一个函数,还需要后续去执行调用。

实例化继承、

在子类的构造函数中new出一个父类实例,添加额外行为和属性返回回去。

function Daughter_(name, age, hobby) {
        var instance = new Father();
        instance.rap = function() {
            return "女儿会RAP";
        }
        return instance;
    }
    var xiaoli = new Daughter_();
    //可以添加其他属性,并非添加到父类上
    TAG(xiaoli.rap());
    TAG(xiaoli instanceof Daughter_); //false
    TAG(xiaoli instanceof Father); //true
    var test = new Father();
    //缺点无法多继承
    //实例是父类的实例化
拷贝继承

将父类的属性和方法通过prototype拷贝过去

 function Daughter_Copy(name, age, hobby) {
        var father = new Father();
        for (let index in father) {
            Daughter_Copy.prototype[index] = father[index];
        }
        this.rap = function() {
                return "女儿照样会rap";
            }
            // this.eat = function() {
            //     return "到底是谁在吃";
            // }
    }

    var xiaolan = new Daughter_Copy();
    TAG(xiaolan.eat());
    TAG(xiaolan instanceof Daughter_Copy); //false
    TAG(xiaolan instanceof Father); //true
    //支持多继承
    //效率较低,内存占用高(因为要拷贝父类的属性)
    // 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

拷贝继承有点类似call调用,但是call里面的方法是直接将引用删除了。

组合继承

耦合

各模块之间互相联系,调用,比如说常见的三层模块,业务逻辑,UI层,数据模型(实体类)互相联系。

解耦

把关联依赖降到最低。

编写良好JS的方法

www.cnblogs.com/humin/p/432…

Array.sort是用的什么排序方式

函数

构造函数

构造函数没有形参,可以直接用,也就是说var p = new Person()

var p = new Person;等价

JS的执行机制(同步和异步)

先执行同步任务,顺序执行代码,遇到异步代码,比如注册事件和需要回调的函数,放入任务队列中,执行完同步任务之后,再将消息队列中的任务放入栈中去执行。

www.jb51.net/article/107…

Location对象

url,统一资源定位符,uniform resource locator

url的语法格式。

protocal://host[:port]/path/[?query]#fragment

input输入框得到值乱码

www.cnblogs.com/kennyliu/p/…

decodeUrl();

这是iso的码?

js的map

HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

\1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。

\2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

\3. 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。

\4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

\5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。

要注意的一些重要术语:

  1. sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

  2. Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

  3. 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

我们能否让HashMap同步?

HashMap可以通过下面的语句进行同步: Map m = Collections.synchronizeMap(hashMap);

结论

Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

__proto__和prototype的区别

__proto__针对每个对象而言,是其中的属性,表示原型对象。

假设有一个对象P,需要调用P里的属性,如果P没有,就会去__proto__里找,相当于找到P的父类,一般是object,如果__proto__没有,那么就去object__proto__指向的null里去找,如果null里面也没有,就去undefined找。但这一步在浏览器里看不到,object的__proto__就是object的函数,在函数f()里去找仍然是object。

是个循环。

console.log(p.__proto__);//object

每个函数有一个prototype的属性,而函数在js里又是对象,所以每个函数拥有prototype和__proto__属性

prototype的汉所以是函数的原型对象,但每个函数都会返回他自身的一个对象,由此,每个函数都可以是构造函数。

var f1 = new FOO();
foo的父类是object

f1.proto === Foo.prototype

FOO()这个函数的prototype 和f1 的 __proto__是同一指向

object都有一个构造器和__proto__属性,而object的属性仍然有构造器constructor 和各种函数

__proto__指向的是Object ,拥有的属性如下

__proto__的作用是向上查找被继承的属性值,

prototype的作用是向上查找被继承的属性和方法

constructor属性才是对象才拥有的,它是一个对象指向一个函数,指该对象的构造函数

constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function() {
            console.log('我会唱歌');
            that = this;
        }
        var ldh = new Star('刘德华', 18);
        // 1. 在构造函数中,里面this指向的是对象实例 ldh
        ldh.sing();
        console.log(that === ldh);

![](E:\前端总结\images\what's the object point .png)

jS运行的环境v8引擎安装(未完成)

www.cnblogs.com/liuning8023…

www.cnblogs.com/lzpong/p/58…

JS引擎深度分析

blog.csdn.net/liujiandu10…

V8 javascript 引擎

www.cnblogs.com/weirdoQi/p/…

从Chrome源码看JS Array的实现

blog.csdn.net/zdy0_2004/a…

GYP构建系统总结

箭头函数(Lambda表达式)

与普通函数的区别

没有this ,argument,super,new.target适用一些需要匿名函数的地方,比普通函数更适合,并且箭头函数不能作为构造函数,因为没有this

箭头函数的this是指向当前声明它的代码块,window

function (arg1,arg2……argn){
//函数声明
}

(arg1,arg2……argn)=>{
	//函数声明
}

//单一参数
(arg1) =>{
    //函数声明
}
arg1 =>{
    //函数声明
}
//无参数
()=>{}

js遍历对象

js获取当前页请求的方式

get方式

通过location.search获取整个连接,进行操作。数据类型这样,自行操作

Location {replace: ƒ, assign: ƒ, href: "http://baixiu.com/list.php?categories=2", ancestorOrigins: DOMStringList, origin: "http://baixiu.com", …}
ancestorOrigins: DOMStringList {length: 0}
assign: ƒ ()
hash: ""
host: "baixiu.com"
hostname: "baixiu.com"
href: "http://baixiu.com/list.php?categories=2"
origin: "http://baixiu.com"
pathname: "/list.php"
port: ""
protocol: "http:"
reload: ƒ reload()
replace: ƒ ()
search: "?categories=2"
toString: ƒ toString()
valueOf: ƒ valueOf()
Symbol(Symbol.toPrimitive): undefined
__proto__: Location

JS中sessionStroge、localStorage、Cookie的区别

sessionStroge刷新界面仍然存在,关闭界面之后,就清空。

图片渲染空白解决问题

从服务端拿到文件后,去渲染,出现短暂空白问题

clearTimer是立即停止定时器

不是等待定时器的函数执行完毕再去停止,而是立马停止

js变量转换

string 转为int的几种方式

parseInt()这个方法可以转换,但是还有一种方式。

            var str = "35-45";
            var size = str.split('-');
            console.log(typeof(size[0]));//string

            var i = size[0];
            console.log(typeof(i));//string

赋值操作之后都还是string类型

            var str = "35-45";
            var size = str.split('-');
            console.log(typeof(size[0]));//string
            var i = size[0];
            i++;
            console.log(typeof(i));//number i--操作相同
            console.log(typeof(size[0]));//string
            var i = size[0];
            i = i + 3;
            console.log(typeof(i));//string
            console.log(i);//353
            console.log(typeof(size[0]));//string
            var i = size[0];
            i = i * 3;
            console.log(typeof(i));//number
            console.log(i);//105

Blob对象

BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的“容器”。一个Blob对象表示一个不可变的, 原始数据的类似文件对象

object对象

toString

valueof

会将对象转换为基本类型,如果无法转换为基本类型,则返回原对象

如何让a==1&&a==2&&a==3为true

	var a = {
		i:1,
		valueOf:function(){
			console.log(this.i)
			return this.i++
		}
	}
	if(a==1&&a==2&&a==3){
		console.log("true")
	}

typeof 的解密

typeof target === 'object' 和 typeof null ==='object'

      let obj = {
        name: "主打",
        age: 2131
      };
      console.log(typeof obj);//object
      console.log(typeof typeof obj);//string

typeof本身返回的是String类型

typeof null的值是object,那么为什么typeof null 的值是object?

《你不知道的javascript》中写到:

原理是这样的,不同的对象在底层都表示为二进制,在javascript中要是二进制前三位都是0的话就表示对象,而null的二进制都是0,那么前三位自然也是0,就被认为是object,所以typeof null 返回的是object

引用类型和值类型的区别

引用类型是指向堆的一块区域,比如foo变量存储在栈,指向对象{n : 1}的弟子,对象{n:1}的所有属性都存储在堆,也有可能指回栈

		var foo={n:1};
		(function (foo) {
			console.log(foo.n);//1 此时调用的是全局对象foo.n foo是对象,声明了一块
			foo.n=3;
			var foo={n:2};
			console.log(foo.n);//2
		})(foo);
		console.log(foo.n);//3

值类型是指向栈,因为这两个的存储方向不一样,foo变量存储在栈,类型指向栈

		var foo=1;
		(function (foo) {
			console.log(foo);
			foo=3;
			var foo=2;
			console.log(foo);
		})(foo);
		console.log(foo);

Object.create 和 {}的区别

vue源码 中经常使用Object.create(null)

Object.create(proto,[propertiesObject])
复制代码
  • proto:新创建对象的原型对象
  • propertiesObject:可选。要添加到新对象的可枚举(新添加的属性是其自身的属性,而不是其原型链上的属性)的属性。

create(null)和{}的区别在于

create(null)创建的对象上没有任何原型,也就是没有父类Object

{}创建的对象是继承于Obejct,可以使用{}.toString,hasOwnProperty,.valueOf方法等

var o = Object.create(Object.prototype,{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})

var o = Object.create({},{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})
上诉代码与创建var o = {}的对象等价

什么时候使用create(null)和{}

create(null)需要一个干净且高度可定制的对象作为数据字典时,想节省hasOwnProperty带来的一丢丢性能丢失使用,其他时候用{}