Skip to content

js++

January 12, 2023 | 07:38 PM

js++

劝君专注案前事,亦是杯酒敬前程

预编译

  1. 检查通篇的语法错误
  2. 解释一行,执行一行
test() // 1
function test(){
    console.log(1)
}

console.log(a) // undefined
var a = 10;

暗示全局变量

function test(){
	a = 10;
    var c = b = 1;
}
test();
console.log(a);// 10
console.log(b);// 1
console.log(c);//c is not defined
console.log(window.c); // undefined
var a = 10;
console.log(window.a);//10

函数上下文

  1. 创建AO(Activation Object)对象,又叫执行期上下文;
  2. 寻找形式参数和变量声明作为AO的属性名,并赋值为undefined;
  3. 传入实际参数的值;
  4. 在函数体内寻找函数声明,放入作为AO的属性,并赋值为其函数体。
function test(a){
    console.log(a)
    var a = 1;
    console.log(a)
    function a (){}
    console.log(a)
    var b = function(){}
    console.log(b)
    function d(){}
}
test(2)
/*
	function a (){}
	1
	1
	function(){}
*/
function test(a,b){
 	console.log(a);
    c = 0;
    var c;
    a = 5;
    b = 6;
    console.log(b);
    function b(){};
    function d(){};
    console.log(b);
}
test(1);
AO {
    a:undefined -> 1 ->(执行函数体) -> 5
    b:undefined ->(执行函数体) ->  function b(){} -> 6
    d : undefined -> (执行函数体) -> function d(){} 
    c: undefined -> (执行函数体) -> 0
}

// 输出
1
6
6

全局上下文

var a = 1;
function a(){
    console.log(2)
}
console.log(a) // 1

GO = {
	a:undefined -> function a(){} -> 1
}
console.log(a,b) // function a(){},undefined
function a(){}
var b = function(){}

a = 1;
function test(){
    console.log(a);
    a = 2;
    console.log(a);
    var a = 3;
    console.log(a);
}
test();
var a;
// 输出 
undefined
2
3
function test(){
    console.log(b);
    if(a){
        var b = 2;
    }
    c = 3;
    console.log(c);
}
var a;
test();
a = 1;
console.log(a);

// 输出
undefined
3
1
function test(){
    return a;
    a = 1;
    function a(){}
    var a = 2;
}
console.log(test());// ƒ a(){}

function test(){
    a = 1;
    function a(){};
    var a = 2;
    return a;
}
console.log(test()) // 2
a = 1;
function test(e ){
    function e(){}
    arguments[0] = 2;
    console.log(e);
    if(a){
        var b = 3;
    }
    var c;
    a = 4;
    var a;
    console.log(b);
    f = 5;
    console.log(c);
    console.log(a);
}
var a;
test(1);
console.log(a);
console.log(f);

GO:{
    a:undefined
    test:function test(){...}
    f:5
}
AO:{
    e:undefined
    	1
    	function e (){}
    	2
    a:undefined
    	4
    c:undefined
    b:undefined
}
// 输出
2
undefined
undefined
4
1
5

隐式类型转换

if(typeof(a) && (-true) + (+undefined) + '')
    {
        console.log('通过了')
    }else{
        console.log('没通过')
    }
// 通过了
a = undefined typeof(a) = 'undefined' // string 为正
-true = -1
+undefined = NaN
(-true) + (+undefined) + '' = 'NaN'

作用域,作用域链

function a(){
    console.log(1)
    function b(){
        console.log(2)
        function c(){
        	console.log(3)
        }
        c()
    }
    b()
}
a()
//
1
2
3
// 作用域链过程
a定义: a.[[scope]] -> 0:GO
a执行: a.[[scope]] ->	0:a -> AO
					 1:GO
b定义b.[[scope]] -> 0:a -> AO
					 1:GO
b执行: b.[[scope]] -> 0:b -> AO
    				 1:a -> AO
					 2:GO                     

闭包

function test1() {
    function test2(){
    var b = 2
    console.log(a+b)
}
var a = 1
return test2;
}
var test3 = test1()
test3() //3
function test(){
    var n = 100;
    function add(){
        n ++;
        console.log(n);
    }
    function reduce(){
        n --;
        console.log(n);
    }
    return [add,reduce];
}
var arr = test()
arr[0]() // 101
arr[1]() // 100
function sunSched(){
    var sunSched = '';
    var operation = {
        setSched:function(thing){
            sunSched = thing;
        },
          showSched:function(){
            console.log(thing)
        }
    }
        return operation;
}
function test(){
    var a = 1
    function plus(){
        a ++;
        console.log(a)
}
    window.plus = plus
}
var add = (function(){
    var a = 1;
    function add(){
        a++;
        console.log(a);
    }
    return add;
})()
add()

立即执行函数

// 两种写法
(function(){
    
})();// 常用

(function(){
    
}()) // W3C建议
(function(a,b){
    console.log(a,b)
}(2,4)) // 6
var num = (function(a,b){
	return a + b
}(2,4))
console.log(num) // 6
(function test1(){
    console.log(1)
})() // 1

var test2 = function(){
    console.log(1)
}() // 1

function test3(){
    console.log(1)
}() // 报错
// 转换成表达式
// 函数声明变成表达式的方法
+|-|!|& function test3(){
    console.log(1)
}() //1

function test3(){
    console.log(1)
}(2) // 这样不会报错,因为把(2)识别成表达式 
function test(){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        arr[i] = function(){
            console.log(i)// 打印十个10
        }
    }
    return arr;
}
var myArr = test();
for(var j = 0; j < 10; j ++){
    myArr[j]();
}
// 分析
实际上是一个闭包现象
for循环里的i对于function来说是全局变量function里的i一直都是指向全局变量i的地址test执行完毕后i的地址仍被function所引用
// 怎么改成从0到9依次打印,利用立即执行函数
function test(){   
    var arr = [];
    for(var i = 0; i < 10; i ++){
        (function(j){
         arr[i] = function(){
            console.log(j)
         }            
        })(i)
    }
    return arr;
}
var myArr = test();
for(var j = 0; j < 10; j ++){
    myArr[j]();
}
var a = 10;
if(function b(){}){ //(function b(){}) 是一个表达式 声明不了函数
    a += typeof(b)
}
console.log(a) // 10undefined

逗号运算符

console.log((2,3))// 3
var fn = (
	function test1(){
		return 1
	},
	
	function tets2(){
		return '2'
	}
)()
console.log(typeof(fn)) // string

对象

GO = {
    Car:(function)
    car1:{
    	color:'red',
	    brand:'Benz'
	}
}
AO = {
    this:{
        color:color,
        brand:brand
    }
}
function Car(color,brand){
    this.color = color,
    this.brand = brand
    // 隐式的return this
}
var car1 = new Car('red','Benz')
function Compute(){
    var args = arguments
    this.loop = function(method){
        	let res
            if(method === 'plus'){
                	res = 0;
           		for(var i = 0; i < args.length; i++){
					res += args[i]
        		}
            }else if(method === 'times'){
                res = 1;
           		for(var i = 0; i < args.length; i++){
					res *= args[i]
        		}
            }
        return res
    } 
}

包装类

var b = new Number(1)
console.log(b)
// Number {1}
typeof b 
//'object'
b + 1  // 2 自动拆箱

var a = 1
typeof a // 'number'
a.toString()  // '1' 自动装箱 
var b = 123
b.toString()
/*
	var c = new Number(b)
	return c.toString()
*/
var name = 'languiji';
name += 10;
var type = typeof(name)
if(type.length === 6){
    type.text = string; // new String(type).text = 'string' delete
}
console.log(type.text) // undefined
function Test(){
    var d = 1;
function f(){
    d++;
    console.log(d)
}
this.g = f
}
var test1 = new Test()
test1.g() // 2
test1.g() // 3
var test2 = new Test()
test2.g() // 2
var x = 1,
	y = z = 0;
function add(n){
	return n = n + 1;
}
y = add(x);
function add(n){
	return n = n + 3;
}
z = add(x);
console.log(x,y,z) // 1,4,4

截断数组

var arr = [1,1,23,12,421,1]
arr.length = 3
console.log(arr) // [1,1,23] 截断了数组,只剩下三个元素

原型和原型链

function Person(){}
Person.prototype.name = '张三'
var p1 = {
    name:'李四'
}
var person = new Person()
console.log(person.name) // 张三
person.__proto__ = p1
console.log(person.name) // 李四
Car.prototype.name = 'Benz'
function Car(){}
var car = new Car()
Car.prototype = {
    name:'Mazda'
}
console.log(car.name) // Benz
/*本质代码 在new的时候
	function Car(){
		var this = {
			__proto__:Car.prototype = {
				name:'Benz'
			}			
		}
	}
*/
实例化的时候本质上就是继承了构造函数的属性实例化之后再修改prototype就和实例化对象无关了
(function(){
	var a = 1;
    function Test(){}
    window.Test = Test
})()
var test = new Test()
(function(){
    var Compute = function(opt){
        this.x = opt[0]
        this.y = opt[1]
    }
    Compute.prototype = {
        plus:function(){
            return this.x + this.y
        },
        minus:function(){
            return this.x - this.y
        },
        mul:function(){
            return this.x * this.y
        },
        div:function(){
            return this.x / this.y
        }
    }
    window.Compute = Compute
})()
Professor.prototype.tSkill = 'JAVA'
function Professor(){}
var professor = new Professor()
Teacher.prototype = new Professor
function Teacher(){
    this.mSkill = 'JS'
}
var teacher = new Teacher();

Student.prototype = teacher
function Student(){
    this.pSkill = 'HTML'
}
var student = new Student()
console.log(student.pSkill) // JAVA
function Obj(){}
Obj.prototype.num = 1
var obj1 = Object.create(Obj.prototype)
var obj2 = new Obj()
console.log(obj1)
console.log(obj2) //两种形式完全一样	
var obj1  = Object.create(null)
console.log(obj1) // 未赋值前就是一个空对象{} 没有任何属性
obj1.num = 1
var obj2 = Object.create(obj1)
console.log(obj2)
console.log(obj2.num)
Number.prototype.toString.call(1) // "1"
Object.prototype.toString.call(1) // "[object Number]"

call和apply

function Car(brand, color){
    this.brand = brand
    this.color = color
}
var newCar = {}
Car.call(newCar, 'Benz', 'red')
Car.apply(newCar, ['Benz', 'red'])
console.log(newCar) //{brand: 'Benz', color: 'red'}
function Compute(){
    this.plus = function(a,b){
        console.log(a + b)
    }
    this.minus = function(a,b){
        console.log(a - b)
    }
}
function FullCompute(){
    Compute.apply(this)
    this.mul = function(a,b){
        console.log(a * b)
    }
    this.div = function(a,b){
        console.log(a / b)
    }
}
var compute = new FullCompute()
compute.plus)(1,2) // 3
function Car(brand, color, displacement){
    this.brand = brand
    this.color = color
    this.displacement = displacement
    this.info = function(){
        return '排量为' + this.displacement + '的' + this.color + this.brand
    }
}
function Person(opt){
    Car.apply(this, [opt.brand, opt.color, opt.displacement])
    this.name = opt.name
    this.age = opt.age
    this.say = function(){
        console.log('年龄' + this.age + '岁姓名为' + this.name + '买了一辆' + this.info())
    }
}
// Person构造函数借用Car构造函数里的方法和属性来实现买车功能
var p = new Person({
    brand:'奔驰',
    color:'红色',
    displacement:'3.0',
    name:'张三',
    age:'25'
})
p.say()
// 年龄25岁姓名为张三买了一辆排量为3.0的红色奔驰

继承

Professor.prototype = {
    name:'Mr.Zhang',
    tSkill:'JAVA'
}
function Professor(){}
var professor = new Professor()
console.log(professor)
/*
	Professor {}
	[[Prototype]]: Object
	name: "Mr.Zhang"
	tSkill: "JAVA"
	[[Prototype]]: Object
*/
Teacher.prototype = professor
function Teacher(){
    this.name = 'Mr.wang'
    this.mSkill = 'JS/JQ'
}
var teacher = new Teacher()
console.log(teacher)
/*
	Teacher {name: 'Mr.wang', mSkill: 'JS/JQ'}
	mSkill: "JS/JQ"
	name: "Mr.wang"
	[[Prototype]]: Object
	[[Prototype]]: Object
	name: "Mr.Zhang"
	tSkill: "JAVA"
	[[Prototype]]: Object
*/

/*
	把professor的实例给了Teacher的原型,实现了继承(顺着__proto__一路找)
*/
function Teacher(){
	this.name = 'Mr.Li'
    this.tSkill = 'JAVA'
}
Teacher.prototype = {
    pSkill:'JS/JQ'
}
var t = new Teacher()
console.log(t)
function Student(){
    this.name = 'Mr.Wang'
}
function Buffer(){}
Buffer.prototype = Teacher.prototype
var buffer = new Buffer()
Student.prototype = buffer
Student.prototype.age = 18
var s = new Student()
console.log(s)
// 其实就是弄一个不用的对象 做缓冲,就算改 也是改到缓冲的对象,不影响其他
// buffer实例化对象继承了Teacher 而student的prototype指向了buffer对象,当对student.prototype做修改时不影响Teacher
function Teacher(){}
function Student(){}
function Buffer(){}
inherit(Student, Teacher)
var s = new Student()
var t = new Teacher()

function inherit(Target, Origin){
    function Buffer(){}
    Buffer.prototype = Origin.prototype
    Target.prototype = new Buffer()
    Target.prototype.constructor = Target
    Target.prototype.super_class = 	Origin
}

// 最终版 模块化开发
var inherit = (function(){
    var Buffer = function(){}
    return function(Target,Origin){
        Buffer.prototype = Origin.prototype
        Target.prototype = new Buffer()
        Target.prototype.constructor = Target
        Target.prototype.super_class = Origin
    }
})()
function inherit(Target, Origin){
    function Buffer(){}
    Buffer.prototype = Origin.prototype
    Target.prototype = new Buffer()
    Target.prototype.constructor = Target
    Target.prototype.super_class = 	Origin
}

链式调用

var sched = {
    a:function(){
        console.log(1)
        return this
    },
    b:function(){
        console.log(2)
        return this
    },
    c:function(){
        console.log(3)
        return this
    },
    d:function(){
        console.log(4)
        return this
    },        
}
sched.a().b().c().d()// 1 2 3 4

对象属性与遍历

// 常规方法
obj.key
// 老方法
obj['key']
// 内部原理
obj.key -> obj['key']

// 例子
var myLang = {
    No1:'html',
    No2:'CSS',
    No3:'js',
    myStudyingLang:function(num){
        console.log(this.No + num) // NaN 这种取不到,原理如上
        console.log(this['No'+ num]) // 这样可以正常取到
    }
}
var car = {
	brand: 'Benz',
    color: 'red',
    displacement: '3.0',
    lang: '5',
   width: '2.5'
}
for(var key in car){
    // car.key是行不通的,内部是car['key'],结果是undefined
    console.log(key + ':' + car[key])
}
var a = []
console.log(Object.prototype.toString.call(a)) // '[object Array]'

this指向

function test(){
    this.d = 3
}
test()
console.log(this.d) // 3
function Test(){
    this.name = '123'
}
var test = new Test()
AO ={
    this:window -> {} -> {__proto__:Test.prototype,name:'123'}
}
GO = {
    Test:function(){}
    test: {...}
}

caller/callee

function test(a,b,c){
    console.log(arguments.callee.length)//3
    console.log(test.length)//3
	console.log(arguments.length)//2
}
test(1,2)
var sum = (function(n){
	if(n <= 1) return 1
    return n + arguments.callee(n-1)
})(100)
// 立即执行函数求递归
function outer() {
    inner();
}
function inner() {
    console.log(inner.caller)
}
outer();
//ƒ outer() {
//    inner();
//}
function foo(){
	bar.apply(null, arguments) // 相当于bar(arguments)
}
function bar(){
    console.log(arguments)
}
foo(1,2,3)
 // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
undefined == null // true
undefined === null // false
isNaN('100') //false 存在隐式类型转化
parseInt('1a') == 1 // true 遇到非数字就停止
NaN == NaN // false NaN 不等于任何东西
{} == {} // false 引用值比较的是地址

new的实现

function myNew(Con,...args){
    // 创建一个新的空对象
    let obj = {}
    // 将这个空对象的__proto__指向构造函数的原型
    // obj.__proto__ = Con.prototype
    Object.setPrototypeOf(obj,Con.prototype)
    // 将this指向空对象
    let res = Con.apply(obj, args)
    // 对构造函数返回值做判断,然后返回对应的值
    return res instanceof Object ? res : obj
}
var a = 5
function test(){
    a = 0;
    console.log(a)
    console.log(this)
    console.log(this.a)
    var a
    console.log(a)
}
test()
/*
	0
	Window {0: Window, 1: Window, 2: Window, 3: Window, window: Window, self: Window, 		document: document, name: '', location: Location, …}
	5
	0
*/
new test()
/*
	0
	test {}
	undefined
	0
*/

对象克隆

var person1 = {
    name:'张三',
    age:18,
    sex:'male',
    height:100,
    weight:140
}
var person2 = person1
person2.name = '李四'
console.log(person1.name) //李四
// 把原来的值给改变了,实际上person2和person1指向的是同一个内存地址
Object.prototype.named = '陈'
function clone(origin,target){
	for(var key in person1){
        target[key] = origin[key]
    }
}

var person1 = {
    name:'张三',
    age:18,
    sex:'male',
    son:{
        name:'李四'
    },
    arr:[]
}
var person2 = {}
clone(person1,person2)
person1.son.name = '123'
console.log(person2)
/*
age: 18
arr: []
name: "张三"
named: "陈"
sex: "male"
son: {name: '123'}
*/
// son仍然被改变而且原型上的属性也跟着赋给person2,这就是浅拷贝的问题
// 优化版本浅拷贝
function clone(origin, target){
    var tar = target || {}
    for(var key in origin){
        if(origin.hasOwnProperty(key)){
            tar[key] = origin[key]
        }
    }
    return tar
}
var person2 = clone(person1)
person2.name = 'lisi'
person2.son.forth = 'ben'
console.log(person1,person2)
function deepClone(origin, target){
    var target = target || {},
        toStr = Obeject.prototype.toString,
        arrType = '[obejct Array]'
    for(var key in origin){
        if(origin.hasOwnProperty(key)){
            if(typeof(origin[key]) === 'object' && origin[key] !== null){
                if(toStr.call(origin[key]) === arrType){
                    target[key] = []
                }else{
                    target[key] = {}
                }
                deepClone(origin[key],target[key])
            }else{
                target[key] = origin[key]
            }
        }
    }
    return target
}

数组

Array.prototype.myPush = function(){
	for(var i = 0; i < arguments.length; i ++){
		this[this.length] = arguments[i]
	}
    return this.length
}