Apps Script that will create a Google Form, turn it into a quiz

📌 How to use

  1. Go to script.google.com → New project.
  2. Paste this entire script into Code.js.
  3. Run buildJavaScriptQuizForm() once and authorize.
  4. Check the Logs for the edit URL and live URL of the new quiz.
/**
 * Builds a 100-question JavaScript multiple-choice quiz as a Google Form.
 */
function buildJavaScriptQuizForm() {
  const formTitle = 'JavaScript 100-Question Quiz';
  const form = FormApp.create(formTitle)
    .setDescription('100-question JavaScript multiple-choice quiz with explanations.')
    .setIsQuiz(true);

  const QUESTIONS = [
    {
      question: 'What does typeof 42 return?',
      choices: ['"int"', '"number"', '"integer"', '"numeric"'],
      correctIndex: 1,
      explanation: 'In JavaScript, all numeric values (integers and floats) have the type "number".'
    },
    {
      question: 'What is the result of typeof null?',
      choices: ['"null"', '"undefined"', '"object"', '"value"'],
      correctIndex: 2,
      explanation: 'typeof null returns "object" because of a long-standing historical quirk in JavaScript.'
    },
    {
      question: 'What will console.log(1 + "2") output?',
      choices: ['3', '"3"', '"12"', 'NaN'],
      correctIndex: 2,
      explanation: 'When adding a number and a string, JavaScript coerces the number to a string and concatenates, giving "12".'
    },
    {
      question: 'What does NaN stand for?',
      choices: ['Not a Null', 'Not a Number', 'Negative a Number', 'No actual Number'],
      correctIndex: 1,
      explanation: 'NaN is the special numeric value that represents an invalid number result, such as 0 / 0.'
    },
    {
      question: 'Which of these is NOT a primitive type in JavaScript?',
      choices: ['string', 'boolean', 'object', 'symbol'],
      correctIndex: 2,
      explanation: 'object is not a primitive. Primitive types include string, number, boolean, null, undefined, symbol, and bigint.'
    },
    {
      question: 'What will typeof undefined return?',
      choices: ['"null"', '"undefined"', '"object"', '"void"'],
      correctIndex: 1,
      explanation: 'The primitive value undefined has the type "undefined".'
    },
    {
      question: 'Which operator is used for strict equality comparison?',
      choices: ['==', '=', '===', '=>'],
      correctIndex: 2,
      explanation: '=== compares both value and type with no type coercion.'
    },
    {
      question: 'What is the result of "5" == 5?',
      choices: ['true', 'false', 'NaN', 'Throws an error'],
      correctIndex: 0,
      explanation: '== performs type coercion, converting "5" to number 5, so the comparison is true.'
    },
    {
      question: 'What is the result of "5" === 5?',
      choices: ['true', 'false', 'NaN', 'Throws an error'],
      correctIndex: 1,
      explanation: '=== requires both type and value to be the same; string "5" is not equal to number 5.'
    },
    {
      question: 'Which keyword declares a block-scoped variable?',
      choices: ['var', 'let', 'const', 'Both let and const'],
      correctIndex: 3,
      explanation: 'Both let and const are block scoped, while var is function scoped.'
    },
    {
      question: 'What happens if you access a variable declared with let before its declaration?',
      choices: ['Returns undefined', 'Throws a ReferenceError', 'Returns null', 'Returns an empty string'],
      correctIndex: 1,
      explanation: 'let and const are in the temporal dead zone before declaration and cause a ReferenceError if accessed.'
    },
    {
      question: 'What will this code log: console.log(a); var a = 10;',
      choices: ['10', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 1,
      explanation: 'var declarations are hoisted and initialized with undefined, so the log shows undefined.'
    },
    {
      question: 'What will this code log: console.log(b); let b = 10;',
      choices: ['10', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 3,
      explanation: 'Accessing a let variable before declaration produces a ReferenceError due to the temporal dead zone.'
    },
    {
      question: 'What is a closure?',
      choices: [
        'A way to close open files',
        'A function bundled with its lexical environment',
        'A function that always returns another function',
        'A method to end a program'
      ],
      correctIndex: 1,
      explanation: 'A closure is when a function retains access to variables from its outer scope even after that scope has finished.'
    },
    {
      question: 'Which array method adds one or more elements to the end of an array?',
      choices: ['push()', 'pop()', 'shift()', 'unshift()'],
      correctIndex: 0,
      explanation: 'push() appends elements to the end of an array.'
    },
    {
      question: 'Which array method creates a new array without modifying the original?',
      choices: ['push()', 'splice()', 'slice()', 'pop()'],
      correctIndex: 2,
      explanation: 'slice() returns a new array containing a selected portion, leaving the original unchanged.'
    },
    {
      question: 'What is the result of [1, 2, 3].length?',
      choices: ['2', '3', '4', 'undefined'],
      correctIndex: 1,
      explanation: 'The length property of an array is the number of elements, which is 3 here.'
    },
    {
      question: 'Which method converts a JSON string into an object?',
      choices: ['JSON.encode()', 'JSON.parse()', 'JSON.stringify()', 'JSON.toObject()'],
      correctIndex: 1,
      explanation: 'JSON.parse() converts a JSON string into a JavaScript object.'
    },
    {
      question: 'What is the output of console.log(0 == false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 0,
      explanation: '== coerces false to 0, so 0 == false is true.'
    },
    {
      question: 'What is the output of console.log(0 === false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 1,
      explanation: '0 (number) and false (boolean) are different types, so === returns false.'
    },
    {
      question: 'How do you write an arrow function that returns the square of x?',
      choices: ['x => { x * x }', 'x => x * x', '(x) => return x * x', 'x -> x * x'],
      correctIndex: 1,
      explanation: 'For single-expression arrow functions, you can omit braces and return: x => x * x.'
    },
    {
      question: 'What will console.log(typeof (() => {})) output?',
      choices: ['"function"', '"object"', '"arrow"', '"undefined"'],
      correctIndex: 0,
      explanation: 'Arrow functions are still functions in JavaScript, so typeof returns "function".'
    },
    {
      question: 'What does Array.isArray(value) do?',
      choices: [
        'Checks if value is iterable',
        'Checks if value is an array',
        'Checks if value is an object',
        'Converts value to an array'
      ],
      correctIndex: 1,
      explanation: 'Array.isArray() returns true only when the value is an array.'
    },
    {
      question: 'What is the value of Number("hello")?',
      choices: ['"hello"', 'NaN', '0', 'undefined'],
      correctIndex: 1,
      explanation: 'Converting a non-numeric string to a number yields NaN.'
    },
    {
      question: 'Which of these is falsy?',
      choices: ['"0"', '[]', '{}', '""'],
      correctIndex: 3,
      explanation: 'The empty string "" is falsy. "0", [], and {} are truthy.'
    },
    {
      question: 'Which keyword stops a loop immediately?',
      choices: ['exit', 'stop', 'break', 'return'],
      correctIndex: 2,
      explanation: 'break exits the nearest loop or switch statement.'
    },
    {
      question: 'Which keyword skips the current iteration of a loop and continues with the next?',
      choices: ['skip', 'next', 'continue', 'pass'],
      correctIndex: 2,
      explanation: 'continue ends the current iteration and proceeds with the next one.'
    },
    {
      question: 'What is the result of console.log("5" - 2)?',
      choices: ['"52"', '3', 'NaN', '"3"'],
      correctIndex: 1,
      explanation: 'The - operator coerces strings to numbers, so "5" - 2 becomes 5 - 2, which is 3.'
    },
    {
      question: 'What is the result of console.log("5" + 2)?',
      choices: ['"52"', '7', 'NaN', '"7"'],
      correctIndex: 0,
      explanation: 'The + operator with a string performs concatenation, giving "52".'
    },
    {
      question: 'In a browser, what is window?',
      choices: [
        'A built-in array',
        'The global object for browser JavaScript',
        'A reserved name for DOM nodes',
        'A CSS object'
      ],
      correctIndex: 1,
      explanation: 'In browsers, the global scope is represented by the window object.'
    },
    {
      question: 'In strict mode, what happens if you assign to an undeclared variable?',
      choices: [
        'It creates a global variable',
        'It creates a local variable',
        'It throws a ReferenceError',
        'It is silently ignored'
      ],
      correctIndex: 2,
      explanation: 'Strict mode prevents implicit globals and throws a ReferenceError instead.'
    },
    {
      question: 'How do you enable strict mode in a script file?',
      choices: [
        'enable strict;',
        '"use strict"; at the top',
        'use strict; without quotes',
        'strict_mode(true);'
      ],
      correctIndex: 1,
      explanation: 'The directive "use strict"; at the top of a file or function enables strict mode.'
    },
    {
      question: 'What is the output of console.log([] == false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 0,
      explanation: '[] is coerced to "" then to 0, false is also coerced to 0, so == returns true.'
    },
    {
      question: 'What is the value of typeof NaN?',
      choices: ['"nan"', '"number"', '"undefined"', '"object"'],
      correctIndex: 1,
      explanation: 'NaN is a special numeric value, so typeof NaN is "number".'
    },
    {
      question: 'How do you check if a value is NaN without coercion?',
      choices: [
        'value == NaN',
        'value === NaN',
        'isNaN(value)',
        'Number.isNaN(value)'
      ],
      correctIndex: 3,
      explanation: 'Number.isNaN(value) checks specifically for NaN and does not coerce the argument.'
    },
    {
      question: 'What is the result of console.log([] + [])?',
      choices: ['[]', '""', '"[]"', 'NaN'],
      correctIndex: 1,
      explanation: 'Arrays are converted to strings; [].toString() is "", so "" + "" is an empty string.'
    },
    {
      question: 'What does Array.prototype.map() return?',
      choices: ['A new array', 'The modified original array', 'A number', 'An object with keys and values'],
      correctIndex: 0,
      explanation: 'map() returns a new array of transformed elements and does not mutate the original array.'
    },
    {
      question: 'Which method is best for selecting elements that meet a condition?',
      choices: ['forEach()', 'map()', 'filter()', 'reduce()'],
      correctIndex: 2,
      explanation: 'filter() returns a new array containing only elements for which the callback returns true.'
    },
    {
      question: 'What does Array.prototype.forEach() return?',
      choices: ['A new array', 'The original array', 'undefined', 'The number of iterations'],
      correctIndex: 2,
      explanation: 'forEach() is used for side effects and always returns undefined.'
    },
    {
      question: 'What does Array.prototype.reduce() typically do?',
      choices: [
        'Sorts an array',
        'Flattens nested arrays only',
        'Reduces an array to a single value',
        'Filters elements'
      ],
      correctIndex: 2,
      explanation: 'reduce() combines elements using an accumulator to produce a single output value.'
    },
    {
      question: 'Which operator spreads the elements of an iterable?',
      choices: ['...', '*', '&', '=>'],
      correctIndex: 0,
      explanation: 'The spread syntax ... expands elements of an iterable in array literals, function calls, and so on.'
    },
    {
      question: 'What is destructuring in JavaScript?',
      choices: [
        'Removing properties from objects',
        'Assigning properties or elements from objects or arrays into variables',
        'Deleting variables',
        'Breaking a loop'
      ],
      correctIndex: 1,
      explanation: 'Destructuring lets you extract values from arrays or objects into variables with compact syntax.'
    },
    {
      question: 'Which is valid array destructuring?',
      choices: [
        'let {a, b} = [1, 2];',
        'let [a, b] = [1, 2];',
        'let (a, b) = [1, 2];',
        'let [a: 1, b: 2];'
      ],
      correctIndex: 1,
      explanation: 'Array destructuring uses square brackets, for example let [a, b] = [1, 2];'
    },
    {
      question: 'Which is valid object destructuring?',
      choices: [
        'let [name] = { name: "Max" };',
        'let {name} = { name: "Max" };',
        'let (name) = { name: "Max" };',
        'let {name: "Max"};'
      ],
      correctIndex: 1,
      explanation: 'Object destructuring uses curly braces with property names, for example let {name} = obj;'
    },
    {
      question: 'In non strict mode, what is the default value of this inside a regular function call fn() in a browser?',
      choices: ['undefined', 'The global object (window)', 'The function itself', 'null'],
      correctIndex: 1,
      explanation: 'In non strict mode, this in a plain function call refers to the global object (window in browsers).'
    },
    {
      question: 'In an arrow function, this is:',
      choices: [
        'Dynamically bound',
        'Always window',
        'Lexically inherited from the surrounding scope',
        'Always undefined'
      ],
      correctIndex: 2,
      explanation: 'Arrow functions do not have their own this; they capture this from the enclosing scope.'
    },
    {
      question: 'How do you create a new object using a constructor function Person?',
      choices: ['Person()', 'new Person()', 'create Person()', 'Object(Person)'],
      correctIndex: 1,
      explanation: 'The new keyword creates a new object and calls the constructor Person with this bound to that object.'
    },
    {
      question: 'Which prototype is used when calling a method on an array literal like []?',
      choices: ['Object.prototype', 'Array.prototype', 'Function.prototype', 'Prototype.prototype'],
      correctIndex: 1,
      explanation: 'Arrays inherit methods such as push and map from Array.prototype.'
    },
    {
      question: 'What does Object.create(proto) do?',
      choices: [
        'Copies all properties deeply from proto',
        'Creates an object whose internal prototype is proto',
        'Clones proto and adds new methods',
        'Creates a new class from proto'
      ],
      correctIndex: 1,
      explanation: 'Object.create(proto) returns a new object whose [[Prototype]] is proto.'
    },
    {
      question: 'How do you define a class in modern JavaScript?',
      choices: [
        'class Person {}',
        'function class Person {}',
        'Person class {}',
        'new class Person {}'
      ],
      correctIndex: 0,
      explanation: 'ES6 introduced the class syntax: class Person {}.'
    },
    {
      question: 'How do you define a method inside a class?',
      choices: [
        'methodName: function() {}',
        'function methodName() {}',
        'methodName() {}',
        'let methodName() {}'
      ],
      correctIndex: 2,
      explanation: 'Inside a class body, methods are defined as methodName() { } without the function keyword.'
    },
    {
      question: 'How do you create a subclass from a class Parent?',
      choices: [
        'class Child: Parent {}',
        'class Child extends Parent {}',
        'class Child inherits Parent {}',
        'class Child Parent {}'
      ],
      correctIndex: 1,
      explanation: 'Child extends Parent is the ES6 syntax for class inheritance.'
    },
    {
      question: 'Which keyword calls the parent class constructor?',
      choices: ['this()', 'parent()', 'base()', 'super()'],
      correctIndex: 3,
      explanation: 'super() calls the constructor of the parent class from within a subclass.'
    },
    {
      question: 'What does typeof function() {} return?',
      choices: ['"object"', '"function"', '"callable"', '"method"'],
      correctIndex: 1,
      explanation: 'Regular functions have type "function" when evaluated with typeof.'
    },
    {
      question: 'Which statement about promises is true?',
      choices: [
        'A promise can be pending, fulfilled, or rejected',
        'A promise can only be fulfilled',
        'Promises block the main thread',
        'Promises replace all callbacks completely'
      ],
      correctIndex: 0,
      explanation: 'Promises have three main states: pending, fulfilled, and rejected.'
    },
    {
      question: 'What does Promise.resolve(5) create?',
      choices: [
        'A rejected promise with value 5',
        'A fulfilled promise with value 5',
        'A pending promise with value 5',
        'A synchronous value 5 only'
      ],
      correctIndex: 1,
      explanation: 'Promise.resolve(5) returns a promise that is immediately fulfilled with value 5.'
    },
    {
      question: 'How do you attach a success handler to a promise p?',
      choices: ['p.then(onFulfilled)', 'p.success(onFulfilled)', 'p.done(onFulfilled)', 'p.resolve(onFulfilled)'],
      correctIndex: 0,
      explanation: 'then registers callbacks for fulfilled and optionally rejected states.'
    },
    {
      question: 'How do you handle errors in a promise chain?',
      choices: ['With try/catch only', 'With .catch()', 'With .error()', 'With .fail()'],
      correctIndex: 1,
      explanation: 'catch handles rejected promises and errors thrown in previous then handlers.'
    },
    {
      question: 'Which keyword is used to declare an asynchronous function?',
      choices: ['sync', 'await', 'async', 'defer'],
      correctIndex: 2,
      explanation: 'Prefixing a function with async makes it return a promise and allows await inside it.'
    },
    {
      question: 'What does await do inside an async function?',
      choices: [
        'Pauses the whole program',
        'Pauses only that async function until the promise settles',
        'Converts a promise to a callback',
        'Makes the function synchronous'
      ],
      correctIndex: 1,
      explanation: 'await suspends the async function until the awaited promise resolves or rejects, without blocking the event loop.'
    },
    {
      question: 'What happens if an error is thrown inside an async function?',
      choices: [
        'It crashes the browser',
        'It becomes a rejected promise',
        'It is ignored',
        'It becomes a fulfilled promise'
      ],
      correctIndex: 1,
      explanation: 'Errors thrown in async functions reject the returned promise, which can be caught with catch or try/catch around await.'
    },
    {
      question: 'What does setTimeout(fn, 0) do?',
      choices: [
        'Executes fn immediately',
        'Schedules fn after the current call stack clears',
        'Blocks execution until fn completes',
        'Throws an error'
      ],
      correctIndex: 1,
      explanation: 'setTimeout with 0 ms delays fn until after the current stack and microtasks complete.'
    },
    {
      question: 'Which of these is NOT part of the JavaScript language itself but provided by the browser?',
      choices: ['Array', 'Promise', 'document', 'Object'],
      correctIndex: 2,
      explanation: 'document is part of the DOM API, which is provided by browsers, not the core JS language.'
    },
    {
      question: 'How do you select an element with id "main" in the DOM?',
      choices: [
        'document.getElement("main")',
        'document.getElementById("main")',
        'document.query("#main")',
        'document.id("main")'
      ],
      correctIndex: 1,
      explanation: 'getElementById selects the element whose id matches the given string.'
    },
    {
      question: 'Which method selects the first element matching a CSS selector?',
      choices: [
        'document.querySelector()',
        'document.querySelectorAll()[0] only',
        'document.getElementBySelector()',
        'document.selectFirst()'
      ],
      correctIndex: 0,
      explanation: 'querySelector returns the first matching element or null.'
    },
    {
      question: 'How do you add a click event listener to a button element btn?',
      choices: [
        'btn.on("click", fn)',
        'btn.click(fn)',
        'btn.addEventListener("click", fn)',
        'btn.addClick(fn)'
      ],
      correctIndex: 2,
      explanation: 'addEventListener is the standard way to attach event listeners to DOM elements.'
    },
    {
      question: 'What will document.querySelectorAll(".item") return?',
      choices: ['A single element', 'An array', 'A NodeList', 'A string'],
      correctIndex: 2,
      explanation: 'querySelectorAll returns a NodeList, which is an array-like collection of elements.'
    },
    {
      question: 'Which property changes the text inside an element?',
      choices: ['innerText', 'text', 'content', 'valueText'],
      correctIndex: 0,
      explanation: 'innerText (or textContent) is used to set or get the text inside an element.'
    },
    {
      question: 'How do you prevent a form default submit behavior in an event handler?',
      choices: ['event.stop()', 'event.preventDefault()', 'event.cancel()', 'event.stopPropagation()'],
      correctIndex: 1,
      explanation: 'preventDefault stops the default browser action such as submitting a form.'
    },
    {
      question: 'What does event.stopPropagation() do?',
      choices: [
        'Prevents default browser behavior',
        'Stops the event from bubbling up to parent elements',
        'Disables all event listeners',
        'Cancels form submission only'
      ],
      correctIndex: 1,
      explanation: 'stopPropagation prevents the event from propagating to ancestor elements in the DOM.'
    },
    {
      question: 'What is hoisting?',
      choices: [
        'Moving files to the top of a project',
        'JavaScript moving declarations to the top of their scope before execution',
        'Sorting variables alphabetically',
        'Loading external scripts earlier'
      ],
      correctIndex: 1,
      explanation: 'Hoisting is the behavior where declarations appear to be moved to the top of their scope.'
    },
    {
      question: 'Which are hoisted with their full definitions?',
      choices: [
        'Function declarations',
        'Function expressions',
        'Arrow functions assigned to variables',
        'Both function expressions and arrow functions'
      ],
      correctIndex: 0,
      explanation: 'Function declarations are hoisted entirely; function expressions and arrow functions are hoisted as variables only.'
    },
    {
      question: 'What is the output of console.log(hoisted()); function hoisted() { return "Hello"; }',
      choices: ['"Hello"', 'undefined', 'Error', 'null'],
      correctIndex: 0,
      explanation: 'The function declaration hoisted is fully hoisted, so calling it before its definition works and returns "Hello".'
    },
    {
      question: 'What is the output of console.log(x); let x = 5;',
      choices: ['5', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 3,
      explanation: 'Accessing a let variable before declaration triggers a ReferenceError due to the temporal dead zone.'
    },
    {
      question: 'Which method checks if an array includes a certain value?',
      choices: ['arr.contains(value)', 'arr.has(value)', 'arr.includes(value)', 'arr.exists(value)'],
      correctIndex: 2,
      explanation: 'includes returns true if the array contains the specified value.'
    },
    {
      question: 'Which statement about const is true?',
      choices: [
        'const variables cannot be reassigned or mutated at all',
        'const prevents reassigning the binding but object contents can still change',
        'const makes values deeply immutable',
        'const is function scoped'
      ],
      correctIndex: 1,
      explanation: 'const prevents rebinding the variable, but if it refers to an object or array, its contents can still be mutated.'
    },
    {
      question: 'What is the output of const obj = { a: 1 }; obj.a = 2; console.log(obj.a);',
      choices: ['1', '2', 'Error', 'undefined'],
      correctIndex: 1,
      explanation: 'The reference obj is constant but the object is mutable, so its a property can change to 2.'
    },
    {
      question: 'What is a template literal?',
      choices: [
        'A precompiled string',
        'A string defined with backticks that can include expressions with ${ }',
        'A JSON template',
        'A string used only for HTML'
      ],
      correctIndex: 1,
      explanation: 'Template literals use backticks and support interpolation with ${expression} and multiline strings.'
    },
    {
      question: 'Which is a valid template literal?',
      choices: [
        '"Hello ${name}"',
        '\'Hello ${name}\'',
        '`Hello ${name}`',
        'Hello ${name}'
      ],
      correctIndex: 2,
      explanation: 'Interpolation works inside backtick strings only, so `Hello ${name}` is the valid template literal.'
    },
    {
      question: 'How do you export a named function in ES modules?',
      choices: [
        'module.export function myFunc() {}',
        'export function myFunc() {}',
        'exports.myFunc = function() {} in browser modules',
        'export: myFunc()'
      ],
      correctIndex: 1,
      explanation: 'Named exports use the export keyword before declarations, for example export function myFunc() {}.'
    },
    {
      question: 'How do you import a named function myFunc from ./utils.js?',
      choices: [
        'import { myFunc } from "./utils.js";',
        'require("./utils.js").myFunc; in browser modules',
        'import myFunc from "./utils.js"; only',
        'include myFunc from "./utils.js";'
      ],
      correctIndex: 0,
      explanation: 'Named imports use curly braces: import { myFunc } from "./utils.js";'
    },
    {
      question: 'What is the default export import syntax?',
      choices: [
        'import { default } from "./mod.js";',
        'import * as default from "./mod.js";',
        'import something from "./mod.js";',
        'import default("./mod.js");'
      ],
      correctIndex: 2,
      explanation: 'import something from "./mod.js"; imports the module default export as something.'
    },
    {
      question: 'Which tool checks whether a property exists directly on an object (not its prototype chain)?',
      choices: [
        'in operator',
        'obj.hasOwnProperty("prop")',
        'obj.propertyExists("prop")',
        'Object.exists(obj, "prop")'
      ],
      correctIndex: 1,
      explanation: 'hasOwnProperty checks only own properties of the object, not inherited ones.'
    },
    {
      question: 'What is the result of const a = { x: 1 }; const b = a; b.x = 2; console.log(a.x);',
      choices: ['1', '2', 'undefined', 'Error'],
      correctIndex: 1,
      explanation: 'a and b refer to the same object, so changing b.x also changes a.x.'
    },
    {
      question: 'How do you make a shallow copy of an object obj?',
      choices: [
        'const copy = obj;',
        'const copy = Object.copy(obj);',
        'const copy = { ...obj };',
        'const copy = new obj();'
      ],
      correctIndex: 2,
      explanation: 'The spread operator { ...obj } copies own enumerable properties into a new object (shallow copy).'
    },
    {
      question: 'What is event delegation?',
      choices: [
        'Assigning one event to multiple elements at once',
        'Attaching a single event listener to a parent element to handle events from its children',
        'Delegating events from browser to server',
        'Combining multiple events into one'
      ],
      correctIndex: 1,
      explanation: 'Event delegation uses event bubbling so a single listener on a parent can handle events from many child elements.'
    },
    {
      question: 'Which built-in data structure maintains insertion order and uses key value pairs with keys of any type?',
      choices: ['Object', 'Map', 'Set', 'Array'],
      correctIndex: 1,
      explanation: 'Map allows keys of any type and preserves insertion order.'
    },
    {
      question: 'Which structure holds unique values only (no duplicates)?',
      choices: ['Array', 'Map', 'Set', 'Object'],
      correctIndex: 2,
      explanation: 'Set stores unique values; adding the same value again has no effect.'
    },
    {
      question: 'What does "use strict" mainly help with?',
      choices: [
        'Faster network requests',
        'Cleaner syntax highlighting',
        'Catching common mistakes and unsafe actions',
        'Making code run in parallel'
      ],
      correctIndex: 2,
      explanation: 'Strict mode throws errors for unsafe actions such as implicit globals and disallows some problematic behavior.'
    },
    {
      question: 'What does Symbol() create?',
      choices: [
        'A unique immutable value usable as an object key',
        'A string alias',
        'A new numeric data type',
        'A private variable that cannot be accessed'
      ],
      correctIndex: 0,
      explanation: 'Symbols are unique, immutable primitives often used as object property keys.'
    },
    {
      question: 'What is the main difference between == and ===?',
      choices: [
        '=== compares only types',
        '== compares only values',
        '=== compares value and type without coercion',
        'They are identical'
      ],
      correctIndex: 2,
      explanation: '=== performs strict equality (no coercion) while == allows type coercion.'
    },
    {
      question: 'What does Object.freeze(obj) do?',
      choices: [
        'Prevents adding, removing, or changing properties of obj',
        'Prevents only adding new properties',
        'Prevents only deleting properties',
        'Makes deep immutable copies of nested objects'
      ],
      correctIndex: 0,
      explanation: 'A frozen object cannot have its existing properties changed, added, or removed (shallow freeze).'
    },
    {
      question: 'What is the output of console.log(typeof [].constructor)?',
      choices: ['"array"', '"object"', '"function"', '"constructor"'],
      correctIndex: 2,
      explanation: '[].constructor is Array, which is a function, so typeof returns "function".'
    },
    {
      question: 'What does Object.keys(obj) return?',
      choices: [
        'All values of obj',
        'An array of obj own enumerable property names',
        'Prototype chain keys',
        'A Map of key value entries'
      ],
      correctIndex: 1,
      explanation: 'Object.keys returns an array of own enumerable property names.'
    },
    {
      question: 'What is the main difference between for...in and for...of?',
      choices: [
        'for...in iterates values; for...of keys',
        'for...in iterates keys; for...of iterates values of iterables',
        'They are identical',
        'for...of only works on plain objects'
      ],
      correctIndex: 1,
      explanation: 'for...in loops over enumerable property names, while for...of iterates values of iterables like arrays and strings.'
    },
    {
      question: 'What will console.log("2" * "3") log?',
      choices: ['"23"', '6', 'NaN', '"6"'],
      correctIndex: 1,
      explanation: 'The * operator coerces both strings to numbers, so "2" * "3" becomes 2 * 3 which is 6.'
    },
    {
      question: 'What is the output of console.log(Boolean("false"))?',
      choices: ['true', 'false', 'NaN', 'Error'],
      correctIndex: 0,
      explanation: 'Any non empty string is truthy, so Boolean("false") is true.'
    },
    {
      question: 'What is function currying?',
      choices: [
        'Combining two functions into one',
        'Transforming a multi argument function into a series of functions each taking one argument',
        'Removing parameters from a function',
        'Overriding built in functions'
      ],
      correctIndex: 1,
      explanation: 'Currying transforms a function f(a, b, c) into f(a)(b)(c), enabling partial application.'
    },
    {
      question: 'What is the main purpose of JavaScript in web development?',
      choices: [
        'Styling web pages',
        'Structuring content',
        'Adding interactivity and dynamic behavior',
        'Serving web pages from the server'
      ],
      correctIndex: 2,
      explanation: 'HTML structures content, CSS styles it, and JavaScript adds logic, interactivity, and dynamic behavior.'
    }
  ];

  QUESTIONS.forEach(function(q, index) {
    const item = form.addMultipleChoiceItem();
    item.setTitle((index + 1) + '. ' + q.question)
        .setPoints(1);

    const choices = q.choices.map(function(choice, i) {
      return item.createChoice(choice, i === q.correctIndex);
    });

    item.setChoices(choices);

    const correctFb = FormApp.createFeedback()
      .setText('Correct! ' + q.explanation)
      .build();
    const incorrectFb = FormApp.createFeedback()
      .setText('Not quite. ' + q.explanation)
      .build();

    item.setFeedbackForCorrect(correctFb);
    item.setFeedbackForIncorrect(incorrectFb);
  });

  Logger.log('Edit URL: ' + form.getEditUrl());
  Logger.log('Live URL: ' + form.getPublishedUrl());
}