JavaScript Programming

How do you program in JavaScript?

  • … supporting object-oriented, imperative, and functional programming
  • (面向对象、命令式、函数式)
  • Originally programming conventions (i.e. patterns ) rather than language features
    • ECMAScript adding language features (e.g. class, => , etc. )

Object-oriented programming: methods

  • With first class functions a property of an object can be a function
let obj = {count: 0};
obj.increment = function (amount) {
	this.count += amount;
	return this.count;
}
  • Method invocation: calls function and binds this to be object
	obj.increment(1); // returns 1
	obj.increment(3); // returns 4
	

this

  • In methods this will be bound to the object
let o = {oldProp: 'this is an old property'};
o.aMethod = function(){
	this.newProp = "this is a new property";
	return Object.keys(this);   // will contain 'newProp'
}
o.aMethod();   // will return ['oldProp', 'aMethod', 'newProp']
  • In non-method functions :
    • this will be the global object
    • Or if use strict; this will be undefined

functions are objects - can have properties(属性)

function plus() {
	if (plus1.invocations == undefined) {
		plus1.invocations = 0;
	}
	plus1.invocations++;
	return value + 1; 
}
  • plus1.invocations will be the number times(次数) function is called
  • Acts like static/class properties in object-oriented languages

functions are objects: have methods

function func(arg) {
	console.log(this,arg);
}
  • toString() method - return functions as source string
    • func.toString() // returns 'function func(arg) { console.log(this,arg);}'
  • call() method - call function specifying this and arguments
    • func.call({t: 1},2) // prints '{t: 1} 2'
    • apply( ) like call( ) except arguments are passed as an array - func.apply({t: 2}, [2])
    • this is like an extra hidden arguments to a function call and is used that way sometimes
  • blind() method - creates a new function with this and arguments bound
    • let newFunc = func.blind({z: 2}, 3);
    • newFunc() //prints '{z: 2} 3'

Object oriented programming: class

  • Functions are classes in JavaScript: Name the function after class
	function Rectangle(width, height) {
		this.width = width;
		this.height = height;
		this.area = function() {    // not correct way of adding methods
			return this.width*this.height;
		}
	}
	let r = new Rectangle(26, 14);  // {width: 26, height:14}

Functions used in this way are called constructors(构造函数) :
r.constructor.name == 'Rectangle'
console.log®: Rectangle { width: 26, height: 14, area: [Function] }

Object-oriented programming: inheritance

  • JavaScript has the notion of a prototype(原型) object for each instance(实例)
    • prototype objects can have prototype objects forming a prototype chain(原型链) (原型对象可以拥有形成原型链的对象)
    • obj --> proto -->proto --> … -->proto -->null
  • On an object property read access JavaScript will search the up the prototype chain until the property is found(在对象属性的读取和访问上,JS会沿着原型链向上搜索,直到找到该属性)
    • Effectively the properties of an object are its own property inaddtion to all the properties up the prototype chain. This is called prototype-based inheritance. (实际上,一个对象的所有属性是它自己的属性加上所有原型链上的属性。这就是原型链继承)
  • Property updates are different: always create property in object if not found. (属性更新却不同:如果没找到属性,会在对象里创建该属性)

Using prototypes

function Rectangle(width, height) {
	this.width = width;
	this.height = height;
}
Rectangle.prototype.area = function() {
	return this.width*this.height;
}
let r = new Rectangle(26, 14);    // {width: 26, height: 14}
let v = r.area();    // v == 26*14
Object.keys(r) == ['width', 'height']    // own properties

Note: Dynamic - changing prototype will cause all instances to change. (改变原型会改变所有实例)

Prototype versus object instances

let r = new Rectangle(26, 14);
Understand the difference between:

	r.newMethod = function() { console.log('New Method called'); }

And:

	Rectangle.prototype.newMethod = function() {
		console.log('New Method called');
	}

Inheritance

Rectangle.prototype = new Shape(...);

  • If desired property not in Rectangle.prototype then JavaScript will look in Shape.prototype and so on.
    • Can view prototype objects as forming a chain. Lookups(查找) go up the prototype chain.
  • Prototype-based inheritance (基于原型的继承)
    • Single inheritance support (单继承支持 ?啥是单继承)
    • Can be dynamically created and modified (可以动态创建和修改)

ECMAScript version 6 extensions

class Rectangle extends Shape {    // Definition and Inheritance
	constructor(height, width) {
		super(height, width);
		this.height = height;
		this.width = width;
	}
	area() {                       // Method definition
		return this.width*this.height;
	}
	static countRects() {          // static method
		...
	}
}
let r = new Rectangle(10, 20)

React.js example class

class HelloWorld extends React.Component {
	constructor(props) {
		super(props); 
		...
	}
	render() {
		return (
			<div>Hello World</div>
		);
	}
}
class HelloWorld extends React.Component {
	constructor(props) {
		super(props);
		this.clickHandler = this.clickHandler.blind(this);   // what does this do?
		...
	}
	clickHandler() {
		...
	}
	render() {
		return(
			<div onClick={this.handleClick}>Hello World</div>
		);
	}
}

Functional Programming

  • imperative(命令式):
for (let i = 0; i < anArr.length; i++) {
	newArr[i] = anArr[i]*i;
}
  • Functional(函数式):
newArr = anArr.map(function(val, ind){
	return val*ind;
});
  • Can write entire program as functions with no side-effects(可以将整个程序写成函数,没有副作用)

anArr.filter(filterFunc).map(map.Func).reduce(reduceFunc)

Functional Programming - ECMAScript 6

  • Imperative:
for (let i = 0; i < anArr.length; i++) {
	newArr[i] = anArr[i]*i; 
}
  • Functional:
newArr = anArr.map((val, ind) => val*ind);     // Arrow function
  • Can write entire program as functions with no sides-effects

anArr.filter(filterFunc).map(mapFunc).reduce(reduceFunc);
Arrow functions don’t redefine this

We can mostly but not totally avoid functional style

  • Asynchronous events done with callback functions
    • Browser:
		function callbackFunc() { console.log("timeout"); }
		setTimeout(callbackFunc, 3*1000);
	- Server: 
		function callbackFunc(err, data) { console.log(String(data)); }
		fs.readFile('/etc/passwd', callbackFunc);
  • Node.js programming: Write function for HTTP request processing
  • React’s JSX prefers functional style: map(), filter(), ?:

Closures (闭包)

  • An advanced programming language concept you need to know about
let globalVar = 1;
function localFunc(argVar) {
	let localVar = 0;
	function embedFunc() { return ++localVar + argVal + globalVar;}
	return embedFunc;
}
let myFunc = localFunc(10);    // What happens if a call myFunc()? Again?
  • myFunc closure contains argVar, localVar and globalVar

Using Scopes(作用域) and Closures

  • Using Scopes and closures to create modules in early JavaScript
	var i = 1;    // i is global
	...
	function() {
		i++;
		return i;
	}
versus
return (function() {
	var i = 1;        //not global
	...
	function f() {
		i++;
		return i;
	}
	return f;
})();

Using closures for pravite object properties

let myObje = (function() {
	let privateProp1 = 1; let privateProp2 = "test";
	let setPrivate1 = function(vall) { privateProp1 = vall;}
	let compute = function() {return privateProp1 + privateProp2;}
	return {compute: compute; setPrivate1: setPrivate1};
}) ();
typeof myObj;           // 'object'
Object.keys(myObj);     // ['compute', 'setPrivate1']

What does myObj.compute() return?

Beware of this and nested functions

'use strict';
function readFileMethod() {
	fs.readFile(this.fileName, function (err, data) {
		if(!err) {
			console.log(this.fileName, `has length`, data.length);
		}
	});
}
let obj = {fileName: "aFile"; readFile: readFileMethod};
obj.readFile();
  • Generates error on the console.log state since this is undefined

Beware of this and nested functions - work around

'use strict';
function readFileMethod() {
	fs.readFile(this.fileName, (err, data) => {
		if(!err) {
			console.log(this.fileName, 'has length', data.length);
		}
	});
}
let obj = {fileName: "aFile"; readFile: readFileMethod};
obj.readFile();
  • Works since an arrow function doesn’t smash this

Closures can be tricky with imperative code

// Read files './file0' and './file1' and return their length
for (let fileNo = 0; fileNo < 2; fileNo++) {
	fs.readFile('./file' + fileNo, function (err, data) {
		if(!err) {
			console.log('file', fileNo, 'has length', data.length);
		}
	});
}
  • Ends up printing two files to console both starting with:
  • file 2 has length
  • Why?

Stepping through the execution

for (let fileNo = 0; fileNo < 2; fileNo++) {    //execution starts here :fileNo = 0
	fs.readFile('./file' + fileNo, function(err, data) { // 注释1&注释2&注释3:解释看下方
		if(!err) {    // 注释4
			console.log('file', fileNo, 'has length', data.length); //注释5
		}
	});
}
  • 注释1: call the function fs.readFile, before we can we must evaluate(评估) the arguments: the first argument results from the string concatenation operation forming './file0', the second arguments is a function which is passed as a function and its closure containing the variables accessed by the function. In this case only fileNo is accessed by the function so the closure contains fileNo (which is currently 0)
  • 注释2: Note that fs.readFile returns after it has started reading the file but before it has called the callback function. The execution does the fileNo++ and calls back to fs.readFile with an argument of './file1' and a new closure and function. The closure has only fileNo (which is currently 1).
  • 注释3: After creating two function with closures and calling fs.readFile twice the for loop finishes. Some time later in the execution the file reads with finish and fs.readFile will call the function we passed. Recall that fileNo is now 2.
  • 注释4: './file0' is read so our callback starts executing err is falsy so we go to the console.log statement
  • 注释5: When evaluating the arguments to console.log we go to the closure and look at the current value of fileNo. We find it as 2. The result we print the correct data.length but the wrong file number. The same thing happens for the './fileNo1' callback

Broken fix #1 - Add a local variable

for (let fileNo = 0; fileNo < 2; fileNo++) {
	var localFileNo = fileNo;
	fs.readFile('./file' + localFileNo, function(err, data) {
		if(!err) {
			console.log('file', localFileNo, 'has length', data.length);
		}
	});
}
  • Closure for callback now contains localFileNo. Unfortunately when the callback functions run localFileNo will be 1. Better than before since one of the printed lines has the correct fileNo

A fix - Make a private copy of fileNo using a call

function printFileLength(aFileNo) {
	fs.readFile('./file' + aFileNo, function (err, data) {
		if(!err) {
			console.log('file', aFileNo, 'has length', data.length);
		}
	});
}
for (let fileNo = 0; fileNo < 2; fileNo++) {
	printFileLength(fileNo);
}

Note: This works but sometimes it prints the fileO line first and sometimes it prints the file1 line first.

Another fix - Make a private copy of fileNo with let

for (var fileNo = 0; fileNo < 2; fileNo++) {
	let localFileNo = fileNo;
	fs.readFile('./file' + localFileNo, function (err, data) {
		if(!err) {
			console.log('file', localFileNo, 'has length', data.length);
		}
	});
}

Note: Same out-of-order execution as previous fix

JavaScript Object Notation ( JSON )

let obj = { ps: 'str', pn: 1, Pa: [1, 'two', 3, 4], po: { spot: 1}};
let s = JSON.stringify(obj) = 
	'{"ps": "str", "pn": 1, "pa": [1, "two", 3, 4], "po": {"stop": 1}}'
typeof s == 'string'
JSON.parse(s)     // returns object with same properties
  • JSON is the standard format for sending data to and from a browser.

JavaScript: The Bad Parts

Declaring variables on use - Workaround: Force declarations

  • let myVar = 2*typeoVar + 1
    Automatic semicolon insertion - Workaround: Enforce semicolons with checkers
  • return "This is a long string so I put it on its own line";
    Type coerceing equals: == - Workaround: Always use ===, !== instead
    ("" == '0') is false but (0 == '') is true, so is (0 == '0') and (false == '0') is true as is (null == undefined)
    with, eval - Workaround: Don’t use

Some JavaScript idioms

  • Assign a default value
	hostname = hostname || 'localhost';
	port = port || 80;
  • Access a prossibly undefined object property
    let prop = obj && obj.propname;
  • Handing multiple this:
	fs.readFile(this.fileName + fileNo, function (err, data) {
		console.log(this.fileName, fileNo);    // wrong!
	});
-the right way: 
	fs.readFile(this.fileName + fileNo, function(err, data) =>
		console.log(this.fileName, fileNo)
	);
  • Handling multiple this : self
let self = this;
fs.readFile(self.fileName + fileNo, function(err, data) {
	console.log(self.fileName, fileNo);
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值