,闭包,原型链  
call, apply, bind
html模板简单实现


作用域

块级作用域

在C或者大多数类C的编程言语中,都存在着块级作用域,如Java中:

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        int j = 0;
    }
    System.out.print(j); // error 找不到符号
    System.out.print(i); // error 找不到符号
}
C中:
int main() {
   
 int x = 1;
    printf(
"%d, ", x); // 1
    if (1) {
        int x = 2;
        printf("%d, ", x); // 2
    }
    printf("%d\n", x); // 1
}

上面的代码中,最后的print语句是无法访问变量 j 和 i 的,因为这两个变量只在for的()或{}作用域中。

现在我们回到JavaScript的作用域,现在看一个的例子:

(function() {
    if(true) {
        var 9;
    }
    console.dir(a); // output 9

    for(var 02i++) {
        var 3;
    }
    console.dir(i); // output 2
    console.dir(j); // output 3
}());

可以看到三个变量 a, i, j 都能正常地在console中输出值,这就是我们要了解的,JavaScript是没有块级作用域的,直接表现就是,在任何的()和{}中声明的变量,都可以在这个函数中进行访问(如果你的声明在函数中的话)。下面我们就说到函数作用域。



函数作用域

先看例子:

(function() {
    var 'I\'m a';

    function scope() {
        var 'I\'m b';
        console.log(b);
    }
    scope();         // 输出 I'm b
    console.log(a); // 输出 I'm a
    console.log(b); // error: b is not defined
}());

例子中,先定义了a和scope函数,然后调用scope函数,函数中定义了b并输出:I'm b,证明b被成功定义。后面接着成功输出 a,但在console.log(b)中产生了异常,异常信息说明了:b is not defined,b没有被定义。由此可见,调用 scope函数定义的b,在函数之外不能访问,并且超出函数作用域后会被销毁。

变量提升
js的var声明变量有个特点,就是执行一个函数之前,在函数内部定义的变量和函数会提升到函数的最开始的地方。
但是不会在开头赋值,赋值还是在原来的地方。

可能之前有同事会困惑,明明函数已经声明了,为什么有时候可能调得了,有时候又调不了,这可能就是用了表达式的声明方式,并且在声明之前调用失败。
let介绍

闭包

我们先看一下下面的代码

(function() {
   function scope() {
        var "Powerdata";

        console.log(n);
    }

    scope();
    console.log(n);
});


在函数作用域中,我们知道,一个变量,在函数之外是不可被访问的,函数执行完毕后会等待GC销毁。
但是在JS里有一种方法可以使函数内声明的变量在函数之外可以使用的,我们改造一下 :


(function() {

    function scope() {
        var "Powerdata";

        return function () {
            return n;
        };
    }

    var myFunc scope();
    var myFunc();
    console.log(n);
})();

我们在scope里声名了函数returnN,并且在调用scope时返回了它,赋值给getN,我们调用getB得到b。

我们把js里的这个returnN叫做闭包。
换句话说,闭包,是使在某个函数内部声明的变量可以在外部调用的函数。


作用

我们知道Java里是有变量作用域的,例如public, private等等,但是在js里是没有私有变量的,我们可以用闭包来模拟私有变量。我们先看看一个普通的对象声名

var object new Object();
object.count 0;   // 0
object.count++;     // 1
object.count--;     // 0


这个object对象的count属性,我们是可以任意访问没有限制的,这样就很不安全。
我们可以这样创建私有变量:

(function() {
    function createObject() {
        var count 0;
        return {
            incrementfunction() {
                count++;
            },
            decrementfunction() {
                count--;
            },
            valuefunction() {
                return count;
            }
        };
    }

    var object createObject();
    console.log(object.count);  // undefined
    console.log(object.value()); // 0
    object.increment();
    object.increment();
    console.log(object.value());// 2
    object.decrement();
    console.log(object.value());// 1
})();

我们可以看到,我们创建的object对象,直接object.count是访问不了的,只能通过调用object的value函数去取得count变量。我们也只能调用increment和decrement递增或者递减count。
我们在写插件或者模块化方面会经常用到闭包。


原型链

JavaScript是一门面向对象的语言,如果你熟悉基于类的语言的话,可能会觉得JavaScript的继承方式有点怪异,JavaScript是基于原型来继承的。

要了解原型链,我们先来说一下JavaScript创建对象的方式。

var dog1 = {
   name'旺财',
   color'黄色'
};
var dog2 = {
   name'Boby',
   color'白色'
}

在创建多个类似的对象时,这样重复写代码会很麻烦,JavaScript提供了构造函数模式,可以new来实例化对象。


function Dog(namecolor) {
   this.name = name;
   this.color = color;
   this.eat function() {
      console.log('吃');
   };
}

var dog1 new Dog('旺财''黄色');
var dog2 new Dog('Boby''白色');

这样的创建对象方式与Java的类似,但它仅仅需要写构造函数就可以用new来实例化对象。
其中构造函数里的this是指new创建的新对象,属性值在构造函数中初始化。这样写起来方式了,但是有一个弊端,就是每一次创建一个对象的时候,都会重新生成一个相同的eat函数,这个函数并不会公用,这样导致了内存的浪费。

prototype属性

每一个构造函数都有一个prototype属性,当实例化一个对象时,这个对象会继承prototype指向的对象。

(function() {

   function Dog(name, color) {
      this.name = name;
      this.color = color;
   }
   Dog.prototype.eat function() {
      console.log('吃骨头');
   };

   var dog1 new Dog('旺财''黄色');
   var dog2 new Dog('Boby''白色');

   dog1.eat();
   dog2.eat();
})();

我们在定义构造函数的时候不把公共的属性或函数一起定义。把可能不同的属性写在构造函数里,再给构造函数的prototype对象添加一个eat函数。这样,我们在实例化的时候,对象会继承prototype对象,公用一个基对象。
怎么证明实例化的对象是基于原型对象的呢?我们可以在实例化之后修改一下prototype 的内容。

(function() {

   function Dog(name, color) {
      this.name = name;
      this.color = color;
   }

   Dog.prototype.eat function() {
      console.log('吃骨头');
   };

   var dog1 new Dog('旺财''黄色');
   var dog2 new Dog('Boby''白色');

   Dog.prototype.eat function() {
      console.log('吃狗粮');
   };

   dog1.eat();
   dog2.eat();
});


我们可以看到,修改了prototype ,两个通过new创建的对象的eat函数都有变化。所有的实例化对象的eat函数都是同一个,提高了内存利用率。

其实,你访问一个对象的某个属性或者调用函数的时候,这个对象都会在自身或者它的原型对象中寻找对应的属性,如果找不到,会再向上级原型去找,直到找到或者原型为null为止。

可能大家会对这种定义方式不习惯,其实在ES6中,js添加了class,extend等的支持,我们也可以像Java那样定义类,但它还是基于原型链的,它只是写法不一样,封装好了那些复杂的逻辑。

this
我们在写Java或者其他有类的面向对象的语言的时候可以知道,this是调用方法时对应的那个对象。
但是在JavaScript中,调用方法的方式有很多种,这会导致JavaScript的this会有些特殊,下面我们来看几种情况。

this为调用的上级对象
我们看一下例子:

var object = {
   val'Powerdata',
   funfunction() {
      console.log(this.val);
   }
};
//调用的上级对象
object.fun();

在例子中,fun函数中的this是它所在在this对象,所以这里的val是object.val,这一点大家都没问题。

但在这里说明一点,this指定的对象其实不是在声明的时候确定的,而是调用时确定的。就是说,fun函数引用的this不是因为它声明在object内,而是因为调用时,fun前面是object。我们看一下下面的例子:

(function() {
   var object = {
      val'Powerdata',
      funfunction() {
         console.log(this.val);
      }
   };

   // 引用全局window
   var fun2 object.fun;
   fun2();

   // 引用其他对象
   var object2 = {val'abc'};
   object2.fun object.fun;
   object2.fun();

})();

这里有两种情况,一个是在object声名后定义了一个fun2的变量,并引用了object.fun函数,这时调用fun2,我们发现控制台输出的是undefined,未定义。然后我们再看后面,定义了object2,有属性值val=abc,我们在object2里引用object.fun后,调用object2的fun,我们发现输出的是abc。
这证明了刚刚说的一点,函数里的this是在调用时确定的,我们看到,调用fun2的时候,它前面没有用对象去调用,所以找不到val属性,这里this其实是指向了window全局,如果我们定义一个全局的val,这里会输出内容。然后在调object2.fun的时候,输出的就是调用者object2的val属性值。

还有一种是用 new 的情况

(function() {
   var Test function() {
      this.'Powerdata';
   };

   var object new Test();
   console.log(object.a);
})();

在用new实例化对象时,会马上执行构造函数,这时this是new自动创建的一个对象,用构造函数初始化后返回对象。

this在我们写代码时可能很容易碰到下面这种问题。

var object = {
   val'Powerdata',
   funfunction() {
      console.log(this.val);
   }
};

var button document.createElement('button');
button.type 'button';
button.innerText '点我';
// 事件
button.onclick = object.fun;
document.body.appendChild(button);

我们在点击按钮的时候,会调用fun函数,但它并不会输出object.val,因为onclick只引用了fun函数,已经和object没有任何关系,这种情况怎么解决?下面介绍bind,call,apply

bind, call, apply

我们先来看一下事件绑定的问题怎么解决:

(function() {
   var object = {
      val'Powerdata',
      funfunction() {
         console.log(this.val);
      }
   };

   var button document.createElement('button');
   button.type 'button';
   button.innerText '点我';

   var fun2 object.fun.bind(object);
   // 事件
   button.onclick = fun2;
   document.body.appendChild(button);

});

我们在引用函数时,调用一下bind函数,参数为object,bind会返回一个新的绑定了object函数,不管你怎么调用fun2,它的this都是object。bind的作用是把对象绑定到一个函数的this上,关返回这个绑定了对象的函数。
所以处罚click事件时,this就会取到object.val的值。

还有call和apply是什么呢?
这两个函数的作用跟bind类似,但它们返回的并不是函数,我们在调用call或apply的时候,会先绑定第一个参数的对象到函数中,然后再自动调用这个函数,再返回函数的return值。bind并不会执行函数。
看一下示例:

(function() {
   var object = {
      val120,
      funfunction() {
         console.log(this.val);
      }
   };

   var fun2 object.fun;

   console.log('fun2()');
   fun2();

   // var object2 = {val: 'boanda'};

   console.log('un2.call(object)');
   fun2.call(object);

   console.log('fun2.apply(object)');
   fun2.apply(object);

   console.log('fun2.bind(object)()');
   fun2.bind(object)();
})();

我们甚至可以把fun2绑定到其他的对象。
传参
如果我们用call或者用apply这两个函数调用函数的时候,怎么传参呢?其实call和apply不只一个参数。


html模板简单实现
如果你没有用到html模板相关的框架或者插件,如Vue, Angol。我们可以有一个很方便的方式去实现简单的动态生成html。
因为没有用到框架,可以很多人会直接用字符串拼接的方式去实现。但是这种方式会让代码看起来很混乱,维护起来特别不方便,理清html结构特别费劲。
这里有一种比较方便清晰方式给大家提供参考。



// 块级作用域
(function() {
    if(true) {
        var a = 9;
    }
    console.dir(a);

    for(var i = 0; i < 2; i++) {
        var j = 3;
    }
    console.dir(i);
    console.dir(j);
});

// 函数作用域
(function() {
	var a = 'I\'m a';

	function scope() {
		var b = 'I\'m b';
		console.log(b);
	}
	scope();         // 输出 I'm b
	console.log(a); // 输出 I'm a
	console.log(b); // error: b is not defined
});


// 变量提升
(function() {
	var p = 10;
	var fun = function() {
		

		console.log(p);

	// 一堆代码
		if(false) {
			var p = 0;
		}
	};

	fun();
});

// 函数提升
(function() {
	console.log('fun1');
	console.log(fun1);

	console.log('fun2');
	console.log(fun2);

	function fun1() {
		console.log('fun1');
	}

	var fun2 = function() {
		console.log('fun1');
	};
	console.log('fun1');
	console.log(fun1);

	console.log('fun2');
	console.log(fun2);
});

// let 有块作用域并没有提升 ES6
(function() {
	if(true) {
		var j = 0;
		console.log('j: %s', j);
	}
	console.log('j: %s', j);

	if(true) {
		let k = 3;
		console.log('k: %s', k);
	}
	console.log('k: %s', k);
});

// 即时函数 (作用,相互访问的方法,或用命名空间)

(function() {
    // code

})();


// 闭包(函数作用域)
// 函数外部不可访问
(function() {
	function scope() {
        var n = "Powerdata";

        console.log(n);
    }

    scope();
    console.log(n);
});

// 可被外部引用的变量
(function() {

    function scope() {
        var n = "Powerdata";

        var returnN = function () {
            return n;
        };

        return returnN;
    }

    var getN = scope();
    var n = getN();
    console.log(n);
});

// 闭包作用 模拟私有变量
(function() {
	var object = new Object();
	object.count = 0;   // 0
	object.count++;     // 1
	object.count--;     // 0
	// 无法限制此类操作
	object.count += 5;


	function createObject() {
        var count = 0;

        return {
            increment: function() {
                count++;
            },
            decrement: function() {
                count--;
            },
            value: function() {
                return count;
            }
        };
    }

    var object2 = createObject();
    console.log('object2.count: %s', object2.count);  // undefined
    console.log('object2.value(): %s', object2.value()); // 0

    console.log('double increment');
    object2.increment();
    object2.increment();
    console.log('object2.value(): %s', object2.value());// 2

    console.log('decrement');
    object2.decrement();

    console.log('object2.value(): %s', object2.value());// 1
});

// 继承与原型链

// 创建对象
(function() {
	var dog1 = {
		name: '旺财',
		color: '黄色'
	};
	var dog2 = {
		name: 'Boby',
		color: '白色'
	}

	function Dog(name, color) {
		this.name = name;
		this.color = color;
		this.eat = function() {
			console.log('吃');
		};
	}

	var dog1 = new Dog('旺财', '黄色');
	var dog2 = new Dog('Boby', '白色');


});
// prototype
(function() {

	function Dog(name, color) {
		this.name = name;
		this.color = color;
	}
	Dog.prototype.eat = function() {
		console.log('吃骨头');
	};

	var dog1 = new Dog('旺财', '黄色');
	var dog2 = new Dog('Boby', '白色');

	dog1.eat();
	dog2.eat();
});

(function() {

	function Dog(name, color) {
		this.name = name;
		this.color = color;
	}

	Dog.prototype.eat = function() {
		console.log('吃骨头');
	};

	var dog1 = new Dog('旺财', '黄色');
	var dog2 = new Dog('Boby', '白色');

	Dog.prototype.eat = function() {
		console.log('吃狗粮');
	};

	dog1.eat();
	dog2.eat();
});


// Function对象(变量指针,可传递,length属性)



// this (用this注意)
(function() {
	var object = {
		val: 'Powerdata',
		fun: function() {
			console.log(this.val);
		}
	};
	//调用的上级对象
	//object.fun();

	// 引用全局window
	//var fun2 = object.fun;
	//fun2();

	// 引用其他对象
	//var object2 = {val: 'abc'};
	//object2.fun = object.fun;
	//object2.fun();

	// 一般会遇到的问题
	//var button = document.createElement('button');
	//button.type = 'button';
	//button.innerText = '点我';
	// 事件
	//button.onclick = object.fun;
	//document.body.appendChild(button);
});
// 全局
(function() {
	var object = {
		fun: function() {
			this.a = 'abc';
		}
	};

	var fun2 = object.fun;
	fun2();

	console.log(a);
});
// new 
(function() {
	var test = function() {
		this.a = 'Powerdata';
	};

	var object = new test();
	console.log(object.a);
});

// bind
(function() {
	var object = {
		val: 'Powerdata',
		fun: function() {
			console.log(this.val);
		}
	};

	//var o = {val: 'I\'m o'};

	var button = document.createElement('button');
	button.type = 'button';
	button.innerText = '点我';

	var fun2 = object.fun.bind(object);
	// 事件
	button.onclick = fun2;
	document.body.appendChild(button);

});

// call, apply
(function() {
	var object = {
		val: 120,
		fun: function() {
			console.log(this.val);
		}
	};

	var fun2 = object.fun;

	console.log('fun2()');
	fun2();

	// var object2 = {val: 'boanda'};

	console.log('un2.call(object)');
	fun2.call(object);

	console.log('fun2.apply(object)');
	fun2.apply(object);

	console.log('fun2.bind(object)()');
	fun2.bind(object)();
});




// html 模板
(function() {
	var table = document.createElement('table');
	table.style.width = '100%';
	table.style.height = '100%';

	var rowIndex;
	var html = '';

	var data = [{
		index: 1,
		name: '姓名1',
		sex: '性别1'
	}, {
		index: 2,
		name: '姓名2',
		sex: '性别2'
	}, {
		index: 3,
		name: '姓名3',
		sex: '性别1'
	}, {
		index: 4,
		name: '姓名4',
		sex: '性别1'
	}];

	var tr = document.createElement('tr');

	for(rowIndex = 0; rowIndex < data.length; rowIndex++) {
		html += '<tr>';
		html += '<td style="background-color:#'+'abe'+'">'
			+data[rowIndex].index+'</td>';
		html += '<td style="background-color:#'+'edc'+'">'
			+data[rowIndex].name+'</td>';
		html += '<td style="background-color:#'+'eec'+'">'
			+data[rowIndex].sex+'</td>';
		html += '</tr>';
	}
	table.innerHTML = html;
	document.body.appendChild(table);
});



/*var tpl = '<tr>\
				<td style="background-color:#abe">{index}</td>\
				<td style="background-color:#ace">{name}</td>\
				<td style="background-color:#aae">{sex}</td>\
			</tr>';*/
(function() {
	var table = document.createElement('table');
	table.style.width = '100%';
	table.style.height = '100%';

	var rowIndex;
	var html = '';

	var data = [{
		index: 1,
		name: '姓名1',
		sex: '性别1'
	}, {
		index: 2,
		name: '姓名2',
		sex: '性别2'
	}, {
		index: 3,
		name: '姓名3',
		sex: '性别1'
	}, {
		index: 4,
		name: '姓名4',
		sex: '性别1'
	}];
	// . function 支持 
	var useTpl = function(data, tpl) {
		return tpl.replace(/\{(\w+)\}/g, function(str, $1) {
			return data[$1];
		});
	};

	var tpl = document.getElementById('table-row').innerHTML;

	for(rowIndex = 0; rowIndex < data.length; rowIndex++) {
		html += useTpl(data[rowIndex], tpl);
	}
	table.innerHTML = html;
	document.body.appendChild(table);
});

以上是公司内部的一次javascript培训的课程资料记录,还没来得及回顾,先记录在此。



作者:星辰 时间:2016-12-25 浏览 695评论 0 赞 0砸 0 标签: javascript
评论
还可以再输入500个字

请您注意

·自觉遵守:爱国、守法、自律、真实、文明的原则
·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规
·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品
·承担一切因您的行为而直接或间接导致的民事或刑事法律责任
·您在NoteShare上发表的作品,NoteShare有权在网站内保留、转载、引用或者删除
·参与本评论即表明您已经阅读并接受上述条款